aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/gtpu/gtpu_encap.c
diff options
context:
space:
mode:
authorRune E. Jensen <runeerle@wgtwo.com>2022-11-22 10:35:03 +0100
committerOle Tr�an <otroan@employees.org>2023-11-10 14:39:39 +0000
commitf9ab6985d44651b3f92490829e8fad5bac0ceec2 (patch)
tree3c4c556d6fe52cebb79251f82b16f572e086a9b1 /src/plugins/gtpu/gtpu_encap.c
parent5cc67aacf01e8c010ec64a56930f854bcd5d7669 (diff)
gtpu: support non-G-PDU packets and PDU Session
Updated the gtpu plugin code to support the PDU Session user plane protocol, required for 5G, as specified in 3GPP TS 38.415 version 17.0.0. This enables some initial support of 5G gNodeB's with the gtpu plugin. New features: - Basic support for the GTP-U Extension Header Flag. Packets with one extension can now be decapsulated. This enables basic support of the PDU Session user plane protocol (3GPP TS 38.415 version 17.0.0). New tunnels can be created with a PDU enable flag and a 6-bit QoS Flow Identifier (QFI). With this, encapsulated packets will have the PDU Session extension header, and the QFI set. - Ability to forward GTP-U packets that are not handled by the plugin directly. Only GTP-U packets with a message type of 255 (G-PDU) are handled directly. However, 3GPP TS 29.281 defines several other message types like echo and error indication. A new feature is added to optionally forward unknown or unsupported packets to a new IP address. This works separately for unknown GTP-U message types, unknown TEIDs, and packets with an unknown GTP-U header. This allows both echo and error indications from a 5G gNodeB to be handled by a different system outside VPP. - Simple way to get metrics for active tunnels and on tunnel close. In 5G session/tunnel lifetime is often short and created frequently. The normal API becomes too slow and inaccurate when too many tunnels are created and deleted every second. Improvements: - A clean ground structure to handle multiple message type in the future. The code path for G-PDU packets is optimized for performance, representing the typical case. Unsupported GTP-U packets enter a slow path that decodes the nature of the error. This presents a easy hook to handle other message types in the future. - Improved error reporting When using traces there is more details in the tunnel descriptions. - Updated the API with several enums. Fixes: - gtpu0->length field in IPv6 was computed with IPv4 header lengths in the encapsulation code. - vec_set_len (t->rewrite, ...) size was computed with the IPv4 header size also for IPv6 tunnels. Issues: - This PR does not enable full support of the 3GPP specification. In particular it only supports a single QoS/QFI flow for each tunnel. It ignores all incoming extension header flags. - API functions might change again when/if more support of the 3GPP TS 38.415 spec is added. Note that I have bumped the API version to 2.1.0 as it seems to be the correct approach based on my API changes. Type: feature Signed-off-by: Rune E. Jensen <runeerle@wgtwo.com> Change-Id: I91cd2b31f2561f1b3fb1e46c4c34a5a3c71b4625
Diffstat (limited to 'src/plugins/gtpu/gtpu_encap.c')
-rw-r--r--src/plugins/gtpu/gtpu_encap.c218
1 files changed, 177 insertions, 41 deletions
diff --git a/src/plugins/gtpu/gtpu_encap.c b/src/plugins/gtpu/gtpu_encap.c
index 4b7d98786f4..2c3c46a4be2 100644
--- a/src/plugins/gtpu/gtpu_encap.c
+++ b/src/plugins/gtpu/gtpu_encap.c
@@ -199,7 +199,8 @@ gtpu_encap_inline (vlib_main_t * vm,
copy_dst3 = (u64 *) ip4_3;
copy_src3 = (u64 *) t3->rewrite;
- /* Copy first 32 octets 8-bytes at a time */
+ /* Copy first 32 octets 8-bytes at a time (minimum size)
+ * TODO: check if clib_memcpy_fast is better */
#define _(offs) copy_dst0[offs] = copy_src0[offs];
foreach_fixed_header4_offset;
#undef _
@@ -212,19 +213,83 @@ gtpu_encap_inline (vlib_main_t * vm,
#define _(offs) copy_dst3[offs] = copy_src3[offs];
foreach_fixed_header4_offset;
#undef _
- /* Last 4 octets. Hopefully gcc will be our friend */
- copy_dst_last0 = (u32 *)(&copy_dst0[4]);
- copy_src_last0 = (u32 *)(&copy_src0[4]);
- copy_dst_last0[0] = copy_src_last0[0];
- copy_dst_last1 = (u32 *)(&copy_dst1[4]);
- copy_src_last1 = (u32 *)(&copy_src1[4]);
- copy_dst_last1[0] = copy_src_last1[0];
- copy_dst_last2 = (u32 *)(&copy_dst2[4]);
- copy_src_last2 = (u32 *)(&copy_src2[4]);
- copy_dst_last2[0] = copy_src_last2[0];
- copy_dst_last3 = (u32 *)(&copy_dst3[4]);
- copy_src_last3 = (u32 *)(&copy_src3[4]);
- copy_dst_last3[0] = copy_src_last3[0];
+
+ /* Copy last octets */
+ if (_vec_len (t0->rewrite) == 36)
+ {
+ /* Last 4 octets. Hopefully gcc will be our friend */
+ copy_dst_last0 = (u32 *) (&copy_dst0[4]);
+ copy_src_last0 = (u32 *) (&copy_src0[4]);
+ copy_dst_last0[0] = copy_src_last0[0];
+ }
+ else
+ {
+ /* Near last 8 octets. */
+#define _(offs) copy_dst0[offs] = copy_src0[offs];
+ _ (4);
+#undef _
+ /* Last 4 octets. Hopefully gcc will be our friend */
+ copy_dst_last0 = (u32 *) (&copy_dst0[5]);
+ copy_src_last0 = (u32 *) (&copy_src0[5]);
+ copy_dst_last0[0] = copy_src_last0[0];
+ }
+
+ if (_vec_len (t1->rewrite) == 36)
+ {
+ /* Last 4 octets. Hopefully gcc will be our friend */
+ copy_dst_last1 = (u32 *) (&copy_dst1[4]);
+ copy_src_last1 = (u32 *) (&copy_src1[4]);
+ copy_dst_last1[0] = copy_src_last1[0];
+ }
+ else
+ {
+ /* Near last 8 octets. */
+#define _(offs) copy_dst1[offs] = copy_src1[offs];
+ _ (4);
+#undef _
+ /* Last 4 octets. Hopefully gcc will be our friend */
+ copy_dst_last1 = (u32 *) (&copy_dst1[5]);
+ copy_src_last1 = (u32 *) (&copy_src1[5]);
+ copy_dst_last1[0] = copy_src_last1[0];
+ }
+
+ if (_vec_len (t2->rewrite) == 36)
+ {
+ /* Last 4 octets. Hopefully gcc will be our friend */
+ copy_dst_last2 = (u32 *) (&copy_dst2[4]);
+ copy_src_last2 = (u32 *) (&copy_src2[4]);
+ copy_dst_last2[0] = copy_src_last2[0];
+ }
+ else
+ {
+ /* Near last 8 octets. */
+#define _(offs) copy_dst2[offs] = copy_src2[offs];
+ _ (4);
+#undef _
+ /* Last 4 octets. Hopefully gcc will be our friend */
+ copy_dst_last2 = (u32 *) (&copy_dst2[5]);
+ copy_src_last2 = (u32 *) (&copy_src2[5]);
+ copy_dst_last2[0] = copy_src_last2[0];
+ }
+
+ if (_vec_len (t3->rewrite) == 36)
+ {
+ /* Last 4 octets. Hopefully gcc will be our friend */
+ copy_dst_last3 = (u32 *) (&copy_dst3[4]);
+ copy_src_last3 = (u32 *) (&copy_src3[4]);
+ copy_dst_last3[0] = copy_src_last3[0];
+ }
+ else
+ {
+ /* Near last 8 octets. */
+#define _(offs) copy_dst3[offs] = copy_src3[offs];
+ _ (4);
+#undef _
+ /* Last 4 octets. Hopefully gcc will be our friend */
+ copy_dst_last3 = (u32 *) (&copy_dst3[5]);
+ copy_src_last3 = (u32 *) (&copy_src3[5]);
+ copy_dst_last3[0] = copy_src_last3[0];
+ }
/* Fix the IP4 checksum and length */
sum0 = ip4_0->checksum;
@@ -318,7 +383,7 @@ gtpu_encap_inline (vlib_main_t * vm,
copy_src2 = (u64 *) t2->rewrite;
copy_dst3 = (u64 *) ip6_3;
copy_src3 = (u64 *) t3->rewrite;
- /* Copy first 56 (ip6) octets 8-bytes at a time */
+ /* Copy first 56 (ip6) octets 8-bytes at a time (minimum size) */
#define _(offs) copy_dst0[offs] = copy_src0[offs];
foreach_fixed_header6_offset;
#undef _
@@ -331,6 +396,40 @@ gtpu_encap_inline (vlib_main_t * vm,
#define _(offs) copy_dst3[offs] = copy_src3[offs];
foreach_fixed_header6_offset;
#undef _
+
+ /* Copy last octets */
+ if (_vec_len (t0->rewrite) == 64)
+ {
+ /* Last 8 octets. */
+#define _(offs) copy_dst0[offs] = copy_src0[offs];
+ _ (7);
+#undef _
+ }
+
+ if (_vec_len (t1->rewrite) == 64)
+ {
+ /* Last 8 octets. */
+#define _(offs) copy_dst1[offs] = copy_src1[offs];
+ _ (7);
+#undef _
+ }
+
+ if (_vec_len (t2->rewrite) == 64)
+ {
+ /* Last 8 octets. */
+#define _(offs) copy_dst2[offs] = copy_src2[offs];
+ _ (7);
+#undef _
+ }
+
+ if (_vec_len (t3->rewrite) == 64)
+ {
+ /* Last 8 octets. */
+#define _(offs) copy_dst3[offs] = copy_src3[offs];
+ _ (7);
+#undef _
+ }
+
/* Fix IP6 payload length */
new_l0 =
clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)
@@ -466,15 +565,19 @@ gtpu_encap_inline (vlib_main_t * vm,
vlib_add_trace (vm, node, b0, sizeof (*tr));
tr->tunnel_index = t0 - gtm->tunnels;
tr->tteid = t0->tteid;
- }
+ tr->pdu_extension = t0->pdu_extension;
+ tr->qfi = t0->qfi;
+ }
- if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
- {
- gtpu_encap_trace_t *tr =
- vlib_add_trace (vm, node, b1, sizeof (*tr));
- tr->tunnel_index = t1 - gtm->tunnels;
- tr->tteid = t1->tteid;
- }
+ if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ gtpu_encap_trace_t *tr =
+ vlib_add_trace (vm, node, b1, sizeof (*tr));
+ tr->tunnel_index = t1 - gtm->tunnels;
+ tr->tteid = t1->tteid;
+ tr->pdu_extension = t1->pdu_extension;
+ tr->qfi = t1->qfi;
+ }
if (PREDICT_FALSE(b2->flags & VLIB_BUFFER_IS_TRACED))
{
@@ -482,15 +585,19 @@ gtpu_encap_inline (vlib_main_t * vm,
vlib_add_trace (vm, node, b2, sizeof (*tr));
tr->tunnel_index = t2 - gtm->tunnels;
tr->tteid = t2->tteid;
- }
+ tr->pdu_extension = t2->pdu_extension;
+ tr->qfi = t2->qfi;
+ }
- if (PREDICT_FALSE(b3->flags & VLIB_BUFFER_IS_TRACED))
- {
- gtpu_encap_trace_t *tr =
- vlib_add_trace (vm, node, b3, sizeof (*tr));
- tr->tunnel_index = t3 - gtm->tunnels;
- tr->tteid = t3->tteid;
- }
+ if (PREDICT_FALSE (b3->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ gtpu_encap_trace_t *tr =
+ vlib_add_trace (vm, node, b3, sizeof (*tr));
+ tr->tunnel_index = t3 - gtm->tunnels;
+ tr->tteid = t3->tteid;
+ tr->pdu_extension = t3->pdu_extension;
+ tr->qfi = t3->qfi;
+ }
vlib_validate_buffer_enqueue_x4 (vm, node, next_index,
to_next, n_left_to_next,
@@ -532,8 +639,9 @@ gtpu_encap_inline (vlib_main_t * vm,
next0 = t0->next_dpo.dpoi_next_node;
vnet_buffer(b0)->ip.adj_index[VLIB_TX] = t0->next_dpo.dpoi_index;
- /* Apply the rewrite string. $$$$ vnet_rewrite? */
- vlib_buffer_advance (b0, -(word)_vec_len(t0->rewrite));
+ /* Apply the rewrite string. $$$$ vnet_rewrite.
+ * The correct total size is set in ip_udp_gtpu_rewrite() */
+ vlib_buffer_advance (b0, -(word) _vec_len (t0->rewrite));
if (is_ip4)
{
@@ -546,10 +654,26 @@ gtpu_encap_inline (vlib_main_t * vm,
#define _(offs) copy_dst0[offs] = copy_src0[offs];
foreach_fixed_header4_offset;
#undef _
- /* Last 4 octets. Hopefully gcc will be our friend */
- copy_dst_last0 = (u32 *)(&copy_dst0[4]);
- copy_src_last0 = (u32 *)(&copy_src0[4]);
- copy_dst_last0[0] = copy_src_last0[0];
+
+ /* Copy last octets */
+ if (_vec_len (t0->rewrite) == 36)
+ {
+ /* Last 4 octets. Hopefully gcc will be our friend */
+ copy_dst_last0 = (u32 *) (&copy_dst0[4]);
+ copy_src_last0 = (u32 *) (&copy_src0[4]);
+ copy_dst_last0[0] = copy_src_last0[0];
+ }
+ else
+ {
+ /* Near last 8 octets. */
+#define _(offs) copy_dst0[offs] = copy_src0[offs];
+ _ (4);
+#undef _
+ /* Last 4 octets. Hopefully gcc will be our friend */
+ copy_dst_last0 = (u32 *) (&copy_dst0[5]);
+ copy_src_last0 = (u32 *) (&copy_src0[5]);
+ copy_dst_last0[0] = copy_src_last0[0];
+ }
/* Fix the IP4 checksum and length */
sum0 = ip4_0->checksum;
@@ -587,6 +711,16 @@ gtpu_encap_inline (vlib_main_t * vm,
#define _(offs) copy_dst0[offs] = copy_src0[offs];
foreach_fixed_header6_offset;
#undef _
+
+ /* Copy last octets */
+ if (_vec_len (t0->rewrite) == 64)
+ {
+ /* Last 8 octets. */
+#define _(offs) copy_dst0[offs] = copy_src0[offs];
+ _ (7);
+#undef _
+ }
+
/* Fix IP6 payload length */
new_l0 =
clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)
@@ -600,9 +734,9 @@ gtpu_encap_inline (vlib_main_t * vm,
/* Fix GTPU length */
gtpu0 = (gtpu_header_t *)(udp0+1);
- new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain(vm, b0)
- - sizeof (*ip4_0) - sizeof(*udp0)
- - GTPU_V1_HDR_LEN);
+ new_l0 = clib_host_to_net_u16 (
+ vlib_buffer_length_in_chain (vm, b0) - sizeof (*ip6_0) -
+ sizeof (*udp0) - GTPU_V1_HDR_LEN);
gtpu0->length = new_l0;
/* IPv6 UDP checksum is mandatory */
@@ -644,7 +778,9 @@ gtpu_encap_inline (vlib_main_t * vm,
vlib_add_trace (vm, node, b0, sizeof (*tr));
tr->tunnel_index = t0 - gtm->tunnels;
tr->tteid = t0->tteid;
- }
+ tr->pdu_extension = t0->pdu_extension;
+ tr->qfi = t0->qfi;
+ }
vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
to_next, n_left_to_next,
bi0, next0);