From 41d8934677481c633ec13bda65d475449bae189c Mon Sep 17 00:00:00 2001 From: Sukchan Lee Date: Sat, 17 Feb 2024 20:40:08 +0100 Subject: [PATCH] [SMF] Added Bi-Directional Flow (#2909) For bi-directions, the rules are created in the same form as for downlink as shown below, so to apply them for uplink, we need to swap the rules according to the interface. RX : permit out from to GX : permit out from to PFCP : permit out from to RULE : Source Destination TFT : Local REMOTE RX : permit in from to GX : permit out from to PFCP : permit out from to RULE : Source Destination TFT : Local REMOTE --- lib/diameter/gx/message.h | 3 +- lib/ipfw/ogs-ipfw.c | 247 ++++++++++++++--------- lib/ipfw/ogs-ipfw.h | 4 +- lib/pfcp/build.c | 36 +++- lib/pfcp/context.h | 18 +- lib/proto/types.h | 42 +++- lib/sbi/conv.c | 3 + src/smf/binding.c | 6 +- src/smf/context.c | 27 ++- src/smf/context.h | 2 + src/smf/gsm-handler.c | 8 +- src/smf/gx-handler.c | 7 +- src/smf/gx-path.c | 5 +- src/smf/npcf-handler.c | 14 +- src/smf/s5c-handler.c | 8 +- tests/common/context.c | 101 ++++++++++ tests/common/context.h | 1 + tests/volte/bearer-test.c | 250 ++++++++++++++++++++++- tests/vonr/qos-flow-test.c | 393 ++++++++++++++++++++++++++++++++++++- 19 files changed, 1037 insertions(+), 138 deletions(-) diff --git a/lib/diameter/gx/message.h b/lib/diameter/gx/message.h index cba5dc2b4..68d1c0726 100644 --- a/lib/diameter/gx/message.h +++ b/lib/diameter/gx/message.h @@ -1,5 +1,5 @@ /* Gx Interface, 3GPP TS 29.212 section 4 - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2024 by Sukchan Lee * * This file is part of Open5GS. * @@ -43,6 +43,7 @@ extern "C" { #define OGS_DIAM_GX_AVP_CODE_FLOW_STATUS (511) #define OGS_DIAM_GX_AVP_CODE_QOS_INFORMATION (1016) #define OGS_DIAM_GX_AVP_CODE_PRECEDENCE (1010) +#define OGS_DIAM_GX_AVP_CODE_RATING_GROUP (432) extern struct dict_object *ogs_diam_gx_application; diff --git a/lib/ipfw/ogs-ipfw.c b/lib/ipfw/ogs-ipfw.c index 0e0e2ced4..0140e94a9 100644 --- a/lib/ipfw/ogs-ipfw.c +++ b/lib/ipfw/ogs-ipfw.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2024 by Sukchan Lee * * This file is part of Open5GS. * @@ -395,117 +395,167 @@ void ogs_pf_content_from_ipfw_rule( * Network support Local Address in TFTs. */ - if (rule->ipv4_src && (direction == OGS_FLOW_DOWNLINK_ONLY)) { - content->component[j].type = OGS_PACKET_FILTER_IPV4_REMOTE_ADDRESS_TYPE; - content->component[j].ipv4.addr = rule->ip.src.addr[0]; - content->component[j].ipv4.mask = rule->ip.src.mask[0]; - j++; len += 9; - } - - if (rule->ipv4_src && (direction == OGS_FLOW_UPLINK_ONLY) && - !no_ipv4v6_local_addr_in_packet_filter) { - content->component[j].type = OGS_PACKET_FILTER_IPV4_LOCAL_ADDRESS_TYPE; - content->component[j].ipv4.addr = rule->ip.src.addr[0]; - content->component[j].ipv4.mask = rule->ip.src.mask[0]; - j++; len += 9; - } - - if (rule->ipv4_dst && (direction == OGS_FLOW_DOWNLINK_ONLY) && - !no_ipv4v6_local_addr_in_packet_filter) { - content->component[j].type = OGS_PACKET_FILTER_IPV4_LOCAL_ADDRESS_TYPE; - content->component[j].ipv4.addr = rule->ip.dst.addr[0]; - content->component[j].ipv4.mask = rule->ip.dst.mask[0]; - j++; len += 9; - } - - if (rule->ipv4_dst && (direction == OGS_FLOW_UPLINK_ONLY)) { - content->component[j].type = OGS_PACKET_FILTER_IPV4_REMOTE_ADDRESS_TYPE; - content->component[j].ipv4.addr = rule->ip.dst.addr[0]; - content->component[j].ipv4.mask = rule->ip.dst.mask[0]; - j++; len += 9; - } - - if (rule->ipv6_src && (direction == OGS_FLOW_DOWNLINK_ONLY)) { - if (no_ipv4v6_local_addr_in_packet_filter) { + if (rule->ipv4_src) { + switch (direction) { + case OGS_FLOW_DOWNLINK_ONLY: + case OGS_FLOW_BIDIRECTIONAL: content->component[j].type = - OGS_PACKET_FILTER_IPV6_REMOTE_ADDRESS_TYPE; - memcpy(content->component[j].ipv6_mask.addr, - rule->ip.src.addr, sizeof rule->ip.src.addr); - memcpy(content->component[j].ipv6_mask.mask, - rule->ip.src.mask, sizeof rule->ip.src.mask); - j++; len += 33; - } else { - content->component[j].type = - OGS_PACKET_FILTER_IPV6_REMOTE_ADDRESS_PREFIX_LENGTH_TYPE; - memcpy(content->component[j].ipv6.addr, - rule->ip.src.addr, sizeof rule->ip.src.addr); - content->component[j].ipv6.prefixlen = - contigmask((uint8_t *)rule->ip.src.mask, 128); - j++; len += 18; + OGS_PACKET_FILTER_IPV4_REMOTE_ADDRESS_TYPE; + content->component[j].ipv4.addr = rule->ip.src.addr[0]; + content->component[j].ipv4.mask = rule->ip.src.mask[0]; + j++; len += 9; + break; + case OGS_FLOW_UPLINK_ONLY: + if (!no_ipv4v6_local_addr_in_packet_filter) { + content->component[j].type = + OGS_PACKET_FILTER_IPV4_LOCAL_ADDRESS_TYPE; + content->component[j].ipv4.addr = rule->ip.src.addr[0]; + content->component[j].ipv4.mask = rule->ip.src.mask[0]; + j++; len += 9; + } + break; + default: + ogs_fatal("Unsupported direction [%d]", direction); + ogs_assert_if_reached(); } } - if (rule->ipv6_src && (direction == OGS_FLOW_UPLINK_ONLY)) { - if (!no_ipv4v6_local_addr_in_packet_filter) { + if (rule->ipv4_dst) { + switch (direction) { + case OGS_FLOW_DOWNLINK_ONLY: + case OGS_FLOW_BIDIRECTIONAL: + if (!no_ipv4v6_local_addr_in_packet_filter) { + content->component[j].type = + OGS_PACKET_FILTER_IPV4_LOCAL_ADDRESS_TYPE; + content->component[j].ipv4.addr = rule->ip.dst.addr[0]; + content->component[j].ipv4.mask = rule->ip.dst.mask[0]; + j++; len += 9; + } + break; + case OGS_FLOW_UPLINK_ONLY: content->component[j].type = - OGS_PACKET_FILTER_IPV6_LOCAL_ADDRESS_PREFIX_LENGTH_TYPE; - memcpy(content->component[j].ipv6.addr, - rule->ip.src.addr, sizeof rule->ip.src.addr); - content->component[j].ipv6.prefixlen = - contigmask((uint8_t *)rule->ip.src.mask, 128); - j++; len += 18; + OGS_PACKET_FILTER_IPV4_REMOTE_ADDRESS_TYPE; + content->component[j].ipv4.addr = rule->ip.dst.addr[0]; + content->component[j].ipv4.mask = rule->ip.dst.mask[0]; + j++; len += 9; + break; + default: + ogs_fatal("Unsupported direction [%d]", direction); + ogs_assert_if_reached(); } } - if (rule->ipv6_dst && (direction == OGS_FLOW_DOWNLINK_ONLY)) { - if (!no_ipv4v6_local_addr_in_packet_filter) { - content->component[j].type = - OGS_PACKET_FILTER_IPV6_LOCAL_ADDRESS_PREFIX_LENGTH_TYPE; - memcpy(content->component[j].ipv6.addr, - rule->ip.dst.addr, sizeof rule->ip.dst.addr); - content->component[j].ipv6.prefixlen = - contigmask((uint8_t *)rule->ip.dst.mask, 128); - j++; len += 18; - } - } - - if (rule->ipv6_dst && (direction == OGS_FLOW_UPLINK_ONLY)) { - if (no_ipv4v6_local_addr_in_packet_filter) { - content->component[j].type = + if (rule->ipv6_src) { + switch (direction) { + case OGS_FLOW_DOWNLINK_ONLY: + case OGS_FLOW_BIDIRECTIONAL: + if (no_ipv4v6_local_addr_in_packet_filter) { + content->component[j].type = OGS_PACKET_FILTER_IPV6_REMOTE_ADDRESS_TYPE; - memcpy(content->component[j].ipv6_mask.addr, + memcpy(content->component[j].ipv6_mask.addr, + rule->ip.src.addr, sizeof rule->ip.src.addr); + memcpy(content->component[j].ipv6_mask.mask, + rule->ip.src.mask, sizeof rule->ip.src.mask); + j++; len += 33; + } else { + content->component[j].type = + OGS_PACKET_FILTER_IPV6_REMOTE_ADDRESS_PREFIX_LENGTH_TYPE; + memcpy(content->component[j].ipv6.addr, + rule->ip.src.addr, sizeof rule->ip.src.addr); + content->component[j].ipv6.prefixlen = + contigmask((uint8_t *)rule->ip.src.mask, 128); + j++; len += 18; + } + break; + case OGS_FLOW_UPLINK_ONLY: + if (!no_ipv4v6_local_addr_in_packet_filter) { + content->component[j].type = + OGS_PACKET_FILTER_IPV6_LOCAL_ADDRESS_PREFIX_LENGTH_TYPE; + memcpy(content->component[j].ipv6.addr, + rule->ip.src.addr, sizeof rule->ip.src.addr); + content->component[j].ipv6.prefixlen = + contigmask((uint8_t *)rule->ip.src.mask, 128); + j++; len += 18; + } + break; + default: + ogs_fatal("Unsupported direction [%d]", direction); + ogs_assert_if_reached(); + } + } + + if (rule->ipv6_dst) { + switch (direction) { + case OGS_FLOW_DOWNLINK_ONLY: + case OGS_FLOW_BIDIRECTIONAL: + if (!no_ipv4v6_local_addr_in_packet_filter) { + content->component[j].type = + OGS_PACKET_FILTER_IPV6_LOCAL_ADDRESS_PREFIX_LENGTH_TYPE; + memcpy(content->component[j].ipv6.addr, rule->ip.dst.addr, sizeof rule->ip.dst.addr); - memcpy(content->component[j].ipv6_mask.mask, - rule->ip.dst.mask, sizeof rule->ip.dst.mask); - j++; len += 33; - } else { - content->component[j].type = - OGS_PACKET_FILTER_IPV6_REMOTE_ADDRESS_PREFIX_LENGTH_TYPE; - memcpy(content->component[j].ipv6.addr, - rule->ip.dst.addr, sizeof rule->ip.dst.addr); - content->component[j].ipv6.prefixlen = - contigmask((uint8_t *)rule->ip.dst.mask, 128); - j++; len += 18; + content->component[j].ipv6.prefixlen = + contigmask((uint8_t *)rule->ip.dst.mask, 128); + j++; len += 18; + } + break; + case OGS_FLOW_UPLINK_ONLY: + if (no_ipv4v6_local_addr_in_packet_filter) { + content->component[j].type = + OGS_PACKET_FILTER_IPV6_REMOTE_ADDRESS_TYPE; + memcpy(content->component[j].ipv6_mask.addr, + rule->ip.dst.addr, sizeof rule->ip.dst.addr); + memcpy(content->component[j].ipv6_mask.mask, + rule->ip.dst.mask, sizeof rule->ip.dst.mask); + j++; len += 33; + } else { + content->component[j].type = + OGS_PACKET_FILTER_IPV6_REMOTE_ADDRESS_PREFIX_LENGTH_TYPE; + memcpy(content->component[j].ipv6.addr, + rule->ip.dst.addr, sizeof rule->ip.dst.addr); + content->component[j].ipv6.prefixlen = + contigmask((uint8_t *)rule->ip.dst.mask, 128); + j++; len += 18; + } + break; + default: + ogs_fatal("Unsupported direction [%d]", direction); + ogs_assert_if_reached(); } } if (rule->port.src.low) { if (rule->port.src.low == rule->port.src.high) { - if (direction == OGS_FLOW_DOWNLINK_ONLY) + switch (direction) { + case OGS_FLOW_DOWNLINK_ONLY: + case OGS_FLOW_BIDIRECTIONAL: content->component[j].type = OGS_PACKET_FILTER_SINGLE_REMOTE_PORT_TYPE; - else + break; + case OGS_FLOW_UPLINK_ONLY: content->component[j].type = OGS_PACKET_FILTER_SINGLE_LOCAL_PORT_TYPE; + break; + default: + ogs_fatal("Unsupported direction [%d]", direction); + ogs_assert_if_reached(); + } content->component[j].port.low = rule->port.src.low; j++; len += 3; } else { - if (direction == OGS_FLOW_DOWNLINK_ONLY) + switch (direction) { + case OGS_FLOW_DOWNLINK_ONLY: + case OGS_FLOW_BIDIRECTIONAL: content->component[j].type = OGS_PACKET_FILTER_REMOTE_PORT_RANGE_TYPE; - else + break; + case OGS_FLOW_UPLINK_ONLY: content->component[j].type = OGS_PACKET_FILTER_LOCAL_PORT_RANGE_TYPE; + break; + default: + ogs_fatal("Unsupported direction [%d]", direction); + ogs_assert_if_reached(); + } content->component[j].port.low = rule->port.src.low; content->component[j].port.high = rule->port.src.high; j++; len += 5; @@ -514,21 +564,37 @@ void ogs_pf_content_from_ipfw_rule( if (rule->port.dst.low) { if (rule->port.dst.low == rule->port.dst.high) { - if (direction == OGS_FLOW_DOWNLINK_ONLY) + switch (direction) { + case OGS_FLOW_DOWNLINK_ONLY: + case OGS_FLOW_BIDIRECTIONAL: content->component[j].type = OGS_PACKET_FILTER_SINGLE_LOCAL_PORT_TYPE; - else + break; + case OGS_FLOW_UPLINK_ONLY: content->component[j].type = OGS_PACKET_FILTER_SINGLE_REMOTE_PORT_TYPE; + break; + default: + ogs_fatal("Unsupported direction [%d]", direction); + ogs_assert_if_reached(); + } content->component[j].port.low = rule->port.dst.low; j++; len += 3; } else { - if (direction == OGS_FLOW_DOWNLINK_ONLY) + switch (direction) { + case OGS_FLOW_DOWNLINK_ONLY: + case OGS_FLOW_BIDIRECTIONAL: content->component[j].type = OGS_PACKET_FILTER_LOCAL_PORT_RANGE_TYPE; - else + break; + case OGS_FLOW_UPLINK_ONLY: content->component[j].type = OGS_PACKET_FILTER_REMOTE_PORT_RANGE_TYPE; + break; + default: + ogs_fatal("Unsupported direction [%d]", direction); + ogs_assert_if_reached(); + } content->component[j].port.low = rule->port.dst.low; content->component[j].port.high = rule->port.dst.high; j++; len += 5; @@ -538,4 +604,3 @@ void ogs_pf_content_from_ipfw_rule( content->num_of_component = j; content->length = len; } - diff --git a/lib/ipfw/ogs-ipfw.h b/lib/ipfw/ogs-ipfw.h index 775d8594b..80367a035 100644 --- a/lib/ipfw/ogs-ipfw.h +++ b/lib/ipfw/ogs-ipfw.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2024 by Sukchan Lee * * This file is part of Open5GS. * @@ -68,7 +68,7 @@ char *ogs_ipfw_encode_flow_description(ogs_ipfw_rule_t *ipfw_rule); * Refer to lib/ipfw/ogs-ipfw.h * Issue #338 * - * + * > * RX : permit out from to * GX : permit out from to * PFCP : permit out from to diff --git a/lib/pfcp/build.c b/lib/pfcp/build.c index 56899bd76..663baa3d9 100644 --- a/lib/pfcp/build.c +++ b/lib/pfcp/build.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2024 by Sukchan Lee * * This file is part of Open5GS. * @@ -364,10 +364,19 @@ void ogs_pfcp_build_create_pdr( memset(pfcp_sdf_filter, 0, sizeof(pfcp_sdf_filter)); for (j = 0; j < pdr->num_of_flow && j < OGS_MAX_NUM_OF_FLOW_IN_PDR; j++) { - pfcp_sdf_filter[j].fd = 1; - pfcp_sdf_filter[j].flow_description_len = - strlen(pdr->flow_description[j]); - pfcp_sdf_filter[j].flow_description = pdr->flow_description[j]; + ogs_assert(pdr->flow[j].fd || pdr->flow[j].bid); + + if (pdr->flow[j].fd) { + pfcp_sdf_filter[j].fd = 1; + pfcp_sdf_filter[j].flow_description_len = + strlen(pdr->flow[j].description); + pfcp_sdf_filter[j].flow_description = pdr->flow[j].description; + } + if (pdr->flow[j].bid) { + pfcp_sdf_filter[j].bid = 1; + pfcp_sdf_filter[j].sdf_filter_id = pdr->flow[j].sdf_filter_id; + } + len = sizeof(ogs_pfcp_sdf_filter_t) + pfcp_sdf_filter[j].flow_description_len; @@ -493,10 +502,19 @@ void ogs_pfcp_build_update_pdr( memset(pfcp_sdf_filter, 0, sizeof(pfcp_sdf_filter)); for (j = 0; j < pdr->num_of_flow && j < OGS_MAX_NUM_OF_FLOW_IN_PDR; j++) { - pfcp_sdf_filter[j].fd = 1; - pfcp_sdf_filter[j].flow_description_len = - strlen(pdr->flow_description[j]); - pfcp_sdf_filter[j].flow_description = pdr->flow_description[j]; + ogs_assert(pdr->flow[j].fd || pdr->flow[j].bid); + + if (pdr->flow[j].fd) { + pfcp_sdf_filter[j].fd = 1; + pfcp_sdf_filter[j].flow_description_len = + strlen(pdr->flow[j].description); + pfcp_sdf_filter[j].flow_description = pdr->flow[j].description; + } + if (pdr->flow[j].bid) { + pfcp_sdf_filter[j].bid = 1; + pfcp_sdf_filter[j].sdf_filter_id = pdr->flow[j].sdf_filter_id; + } + len = sizeof(ogs_pfcp_sdf_filter_t) + pfcp_sdf_filter[j].flow_description_len; diff --git a/lib/pfcp/context.h b/lib/pfcp/context.h index 0dcd98e4b..679fc20ff 100644 --- a/lib/pfcp/context.h +++ b/lib/pfcp/context.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2024 by Sukchan Lee * * This file is part of Open5GS. * @@ -186,7 +186,21 @@ typedef struct ogs_pfcp_pdr_s { ogs_pfcp_qer_t *qer; int num_of_flow; - char *flow_description[OGS_MAX_NUM_OF_FLOW_IN_PDR]; + struct { + union { + struct { + ED6(uint8_t spare1:3;, + uint8_t bid:1;, + uint8_t fl:1;, + uint8_t spi:1;, + uint8_t ttc:1;, + uint8_t fd:1;) + }; + uint8_t flags; + }; + char *description; + uint32_t sdf_filter_id; + } flow[OGS_MAX_NUM_OF_FLOW_IN_PDR];; ogs_list_t rule_list; /* Rule List */ diff --git a/lib/proto/types.h b/lib/proto/types.h index 3ae4295d9..fe71c796d 100644 --- a/lib/proto/types.h +++ b/lib/proto/types.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2024 by Sukchan Lee * * This file is part of Open5GS. * @@ -459,9 +459,40 @@ typedef struct ogs_qos_s { int ogs_check_qos_conf(ogs_qos_t *qos); /********************************** - * Flow Structure */ + * TS29.212 + * Ch 5.3.65 Flow-Direction AVP + * + * The Flow-Direction AVP (AVP code 1080) is of type Enumerated. + * It indicates the direction/directions that a filter is applicable, + * downlink only, uplink only or both down- and uplink (bidirectional). + * + * UNSPECIFIED (0) + * The corresponding filter applies for traffic to the UE (downlink), + * but has no specific direction declared. The service data flow detection + * shall apply the filter for uplink traffic as if the filter was + * bidirectional. The PCRF shall not use the value UNSPECIFIED + * in filters created by the network in NW-initiated procedures. + * The PCRF shall only include the value UNSPECIFIED in filters + * in UE-initiated procedures if the same value is received from + * in the CCR request from the PCEF. + * + * DOWNLINK (1) + * The corresponding filter applies for traffic to the UE. + * + * UPLINK (2) + * The corresponding filter applies for traffic from the UE. + * + * BIDIRECTIONAL (3) + * The corresponding filter applies for traffic both to and from the UE. + * + * NOTE: The corresponding filter data is unidirectional. The filter + * for the opposite direction has the same parameters, but having + * the source and destination address/port parameters swapped. + */ +#define OGS_FLOW_UNSPECIFIED 0 #define OGS_FLOW_DOWNLINK_ONLY 1 #define OGS_FLOW_UPLINK_ONLY 2 +#define OGS_FLOW_BIDIRECTIONAL 3 typedef struct ogs_flow_s { uint8_t direction; char *description; @@ -477,7 +508,11 @@ typedef struct ogs_flow_s { } while(0) /********************************** - * PCC Rule Structure */ + * TS29.212 + * Ch 5.3.2 Charging-Rule-Install AVP + * + * PCC Rule Structure + */ typedef struct ogs_pcc_rule_s { #define OGS_PCC_RULE_TYPE_INSTALL 1 #define OGS_PCC_RULE_TYPE_REMOVE 2 @@ -491,6 +526,7 @@ typedef struct ogs_pcc_rule_s { int flow_status; uint32_t precedence; + uint32_t rating_group; ogs_qos_t qos; } ogs_pcc_rule_t; diff --git a/lib/sbi/conv.c b/lib/sbi/conv.c index 5fb072481..e5a83fb89 100644 --- a/lib/sbi/conv.c +++ b/lib/sbi/conv.c @@ -1466,6 +1466,9 @@ OpenAPI_pcc_rule_t *ogs_sbi_build_pcc_rule( else if (flow->direction == OGS_FLOW_DOWNLINK_ONLY) FlowInformation->flow_direction = OpenAPI_flow_direction_DOWNLINK; + else if (flow->direction == OGS_FLOW_BIDIRECTIONAL) + FlowInformation->flow_direction = + OpenAPI_flow_direction_BIDIRECTIONAL; else { ogs_fatal("Unsupported direction [%d]", flow->direction); ogs_assert_if_reached(); diff --git a/src/smf/binding.c b/src/smf/binding.c index 3484bc64e..72ee3e517 100644 --- a/src/smf/binding.c +++ b/src/smf/binding.c @@ -65,7 +65,7 @@ static void gtp_bearer_timeout(ogs_gtp_xact_t *xact, void *data) /* * Issue #338 * - * + * * RULE : Source Destination * TFT : Local REMOTE * @@ -303,7 +303,7 @@ void smf_bearer_binding(smf_sess_t *sess) * Refer to lib/ipfw/ogs-ipfw.h * Issue #338 * - * + * * GX : permit out from to * --> * RULE : Source Destination @@ -632,7 +632,7 @@ void smf_qos_flow_binding(smf_sess_t *sess) * Refer to lib/ipfw/ogs-ipfw.h * Issue #338 * - * + * * GX : permit out from to * --> * RULE : Source Destination diff --git a/src/smf/context.c b/src/smf/context.c index c87df3a38..69f59f3b1 100644 --- a/src/smf/context.c +++ b/src/smf/context.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2024 by Sukchan Lee * * This file is part of Open5GS. * @@ -2569,16 +2569,28 @@ void smf_bearer_tft_update(smf_bearer_t *bearer) ogs_list_for_each(&bearer->pf_list, pf) { if (pf->direction == OGS_FLOW_DOWNLINK_ONLY) { - dl_pdr->flow_description[dl_pdr->num_of_flow++] = + dl_pdr->flow[dl_pdr->num_of_flow].fd = 1; + dl_pdr->flow[dl_pdr->num_of_flow].description = pf->flow_description; - + dl_pdr->num_of_flow++; } else if (pf->direction == OGS_FLOW_UPLINK_ONLY) { - ul_pdr->flow_description[ul_pdr->num_of_flow++] = + ul_pdr->flow[ul_pdr->num_of_flow].fd = 1; + ul_pdr->flow[ul_pdr->num_of_flow].description = pf->flow_description; + ul_pdr->num_of_flow++; + } else if (pf->direction == OGS_FLOW_BIDIRECTIONAL) { + dl_pdr->flow[dl_pdr->num_of_flow].fd = 1; + dl_pdr->flow[dl_pdr->num_of_flow].description = + pf->flow_description; + dl_pdr->flow[dl_pdr->num_of_flow].bid = 1; + dl_pdr->flow[dl_pdr->num_of_flow].sdf_filter_id = pf->sdf_filter_id; + dl_pdr->num_of_flow++; + ul_pdr->flow[ul_pdr->num_of_flow].bid = 1; + ul_pdr->flow[ul_pdr->num_of_flow].sdf_filter_id = pf->sdf_filter_id; + ul_pdr->num_of_flow++; } else { + ogs_fatal("Unsupported direction [%d]", pf->direction); ogs_assert_if_reached(); - ogs_fatal("Flow Bidirectional is not supported[%d]", - pf->direction); } } } @@ -2677,6 +2689,9 @@ smf_pf_t *smf_pf_add(smf_bearer_t *bearer) ogs_assert(pf->precedence > 0 && pf->precedence <= (OGS_MAX_NUM_OF_BEARER * OGS_MAX_NUM_OF_FLOW_IN_BEARER)); + /* Re-use 'pf_precedence_pool' to generate SDF Filter ID */ + pf->sdf_filter_id = *(pf->precedence_node); + pf->bearer = bearer; ogs_list_add(&bearer->pf_list, pf); diff --git a/src/smf/context.h b/src/smf/context.h index 1cef8053f..a9f2b7311 100644 --- a/src/smf/context.h +++ b/src/smf/context.h @@ -161,6 +161,8 @@ ED3(uint8_t spare:2;, uint8_t precedence; /* Only used in EPC */ + uint32_t sdf_filter_id; /* SDF Filter ID */ + uint8_t *identifier_node; /* Pool-Node for Identifier */ uint8_t *precedence_node; /* Pool-Node for Precedence */ diff --git a/src/smf/gsm-handler.c b/src/smf/gsm-handler.c index 058de3260..d01252611 100644 --- a/src/smf/gsm-handler.c +++ b/src/smf/gsm-handler.c @@ -261,7 +261,7 @@ int gsm_handle_pdu_session_modification_request( * Refer to lib/ipfw/ogs-ipfw.h * Issue #338 * - * + * * TFT : Local REMOTE * --> * RULE : Source Destination @@ -280,7 +280,7 @@ int gsm_handle_pdu_session_modification_request( /* * Issue #338 * - * + * * RULE : Source Destination * --> * GX : permit out from to @@ -336,7 +336,7 @@ int gsm_handle_pdu_session_modification_request( * Refer to lib/ipfw/ogs-ipfw.h * Issue #338 * - * + * * TFT : Local REMOTE * --> * RULE : Source Destination @@ -355,7 +355,7 @@ int gsm_handle_pdu_session_modification_request( /* * Issue #338 * - * + * * RULE : Source Destination * --> * GX : permit out from to diff --git a/src/smf/gx-handler.c b/src/smf/gx-handler.c index e40bab731..65b5c9199 100644 --- a/src/smf/gx-handler.c +++ b/src/smf/gx-handler.c @@ -1,5 +1,5 @@ /* Gx Interface, 3GPP TS 29.212 section 4 - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2024 by Sukchan Lee * Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH * * This file is part of Open5GS. @@ -162,8 +162,11 @@ uint32_t smf_gx_handle_cca_initial_request( &ul_pdr->ue_ip_addr, &ul_pdr->ue_ip_addr_len)); /* Set UE-to-CP Flow-Description and Outer-Header-Creation */ - up2cp_pdr->flow_description[up2cp_pdr->num_of_flow++] = + up2cp_pdr->flow[up2cp_pdr->num_of_flow].fd = 1; + up2cp_pdr->flow[up2cp_pdr->num_of_flow].description = (char *)"permit out 58 from ff02::2/128 to assigned"; + up2cp_pdr->num_of_flow++; + ogs_assert(OGS_OK == ogs_pfcp_ip_to_outer_header_creation( &ogs_gtp_self()->gtpu_ip, diff --git a/src/smf/gx-path.c b/src/smf/gx-path.c index 9e64d99e5..b44a1ee6d 100644 --- a/src/smf/gx-path.c +++ b/src/smf/gx-path.c @@ -1,5 +1,5 @@ /* Gx Interface, 3GPP TS 29.212 section 4 - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2024 by Sukchan Lee * * This file is part of Open5GS. * @@ -1570,6 +1570,9 @@ static int decode_pcc_rule_definition( case OGS_DIAM_GX_AVP_CODE_PRECEDENCE: pcc_rule->precedence = hdr->avp_value->i32; break; + case OGS_DIAM_GX_AVP_CODE_RATING_GROUP: + pcc_rule->rating_group = hdr->avp_value->i32; + break; default: ogs_error("Not implemented(%d)", hdr->avp_code); break; diff --git a/src/smf/npcf-handler.c b/src/smf/npcf-handler.c index ff5242797..22441af2e 100644 --- a/src/smf/npcf-handler.c +++ b/src/smf/npcf-handler.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2024 by Sukchan Lee * * This file is part of Open5GS. * @@ -197,10 +197,13 @@ static void update_authorized_pcc_rule_and_qos( else if (FlowInformation->flow_direction == OpenAPI_flow_direction_DOWNLINK) flow->direction = OGS_FLOW_DOWNLINK_ONLY; + else if (FlowInformation->flow_direction == + OpenAPI_flow_direction_BIDIRECTIONAL) + flow->direction = OGS_FLOW_BIDIRECTIONAL; else { - ogs_fatal("Unsupported direction [%d]", + ogs_error("Unsupported direction [%d]", FlowInformation->flow_direction); - ogs_assert_if_reached(); + continue; } flow->description = @@ -542,8 +545,11 @@ bool smf_npcf_smpolicycontrol_handle_create( sess->ipv6 ? OGS_INET6_NTOP(&sess->ipv6->addr, buf2) : ""); /* Set UE-to-CP Flow-Description and Outer-Header-Creation */ - up2cp_pdr->flow_description[up2cp_pdr->num_of_flow++] = + up2cp_pdr->flow[up2cp_pdr->num_of_flow].fd = 1; + up2cp_pdr->flow[up2cp_pdr->num_of_flow].description = (char *)"permit out 58 from ff02::2/128 to assigned"; + up2cp_pdr->num_of_flow++; + ogs_assert(OGS_OK == ogs_pfcp_ip_to_outer_header_creation( &ogs_gtp_self()->gtpu_ip, diff --git a/src/smf/s5c-handler.c b/src/smf/s5c-handler.c index 857c473d6..d194bf09a 100644 --- a/src/smf/s5c-handler.c +++ b/src/smf/s5c-handler.c @@ -1242,7 +1242,7 @@ void smf_s5c_handle_bearer_resource_command( * Refer to lib/ipfw/ogs-ipfw.h * Issue #338 * - * + * * TFT : Local REMOTE * --> * RULE : Source Destination @@ -1260,7 +1260,7 @@ void smf_s5c_handle_bearer_resource_command( /* * Issue #338 * - * + * * RULE : Source Destination * --> * GX : permit out from to @@ -1311,7 +1311,7 @@ void smf_s5c_handle_bearer_resource_command( * Refer to lib/ipfw/ogs-ipfw.h * Issue #338 * - * + * * TFT : Local REMOTE * --> * RULE : Source Destination @@ -1330,7 +1330,7 @@ void smf_s5c_handle_bearer_resource_command( /* * Issue #338 * - * + * * RULE : Source Destination * --> * GX : permit out from to diff --git a/tests/common/context.c b/tests/common/context.c index a4954e252..8d797dfd4 100644 --- a/tests/common/context.c +++ b/tests/common/context.c @@ -1671,6 +1671,107 @@ bson_t *test_db_new_qos_flow(test_ue_t *test_ue) return doc; } +bson_t *test_db_new_qos_flow_bi_directional(test_ue_t *test_ue) +{ + bson_t *doc = NULL; + + ogs_assert(test_ue); + + doc = BCON_NEW( + "imsi", BCON_UTF8(test_ue->imsi), + "msisdn", "[", + BCON_UTF8(TEST_MSISDN), + BCON_UTF8(TEST_ADDITIONAL_MSISDN), + "]", + "ambr", "{", + "downlink", "{", + "value", BCON_INT32(1), + "unit", BCON_INT32(3), + "}", + "uplink", "{", + "value", BCON_INT32(1), + "unit", BCON_INT32(3), + "}", + "}", + "slice", "[", "{", + "sst", BCON_INT32(1), + "default_indicator", BCON_BOOL(true), + "session", "[", "{", + "name", BCON_UTF8("internet"), + "type", BCON_INT32(3), + "ambr", "{", + "downlink", "{", + "value", BCON_INT32(1), + "unit", BCON_INT32(3), + "}", + "uplink", "{", + "value", BCON_INT32(1), + "unit", BCON_INT32(3), + "}", + "}", + "qos", "{", + "index", BCON_INT32(9), + "arp", "{", + "priority_level", BCON_INT32(8), + "pre_emption_vulnerability", BCON_INT32(1), + "pre_emption_capability", BCON_INT32(1), + "}", + "}", + "pcc_rule", "[", "{", + "qos", "{", + "index", BCON_INT32(1), + "arp", "{", + "priority_level", BCON_INT32(2), + "pre_emption_vulnerability", BCON_INT32(2), + "pre_emption_capability", BCON_INT32(2), + "}", + "mbr", "{", + "downlink", "{", + "value", BCON_INT32(64), + "unit", BCON_INT32(1), + "}", + "uplink", "{", + "value", BCON_INT32(44), + "unit", BCON_INT32(1), + "}", + "}", + "gbr", "{", + "downlink", "{", + "value", BCON_INT32(64), + "unit", BCON_INT32(1), + "}", + "uplink", "{", + "value", BCON_INT32(44), + "unit", BCON_INT32(1), + "}", + "}", + "}", + "flow", "[", + "{", "direction", BCON_INT32(3), + "description", BCON_UTF8("permit out icmp from any to assigned"), "}", + "{", "direction", BCON_INT32(3), + "description", BCON_UTF8("permit out udp from 10.200.136.98/32 23455 to assigned 1-65535"), "}", + "]", + "}", "]", + "}", "]", + "}", "]", + "security", "{", + "k", BCON_UTF8(test_ue->k_string), + "opc", BCON_UTF8(test_ue->opc_string), + "amf", BCON_UTF8("8000"), + "sqn", BCON_INT64(64), + "}", + "subscribed_rau_tau_timer", BCON_INT32(12), + "network_access_mode", BCON_INT32(0), + "subscriber_status", BCON_INT32(0), + "operator_determined_barring", BCON_INT32(0), + "access_restriction_data", BCON_INT32(32) + ); + ogs_assert(doc); + + return doc; +} + bson_t *test_db_new_session(test_ue_t *test_ue) { bson_t *doc = NULL; diff --git a/tests/common/context.h b/tests/common/context.h index d92466d07..fd957e08e 100644 --- a/tests/common/context.h +++ b/tests/common/context.h @@ -530,6 +530,7 @@ int test_db_remove_ue(test_ue_t *test_ue); bson_t *test_db_new_simple(test_ue_t *test_ue); bson_t *test_db_new_qos_flow(test_ue_t *test_ue); +bson_t *test_db_new_qos_flow_bi_directional(test_ue_t *test_ue); bson_t *test_db_new_session(test_ue_t *test_ue); bson_t *test_db_new_ims(test_ue_t *test_ue); bson_t *test_db_new_slice_with_same_dnn(test_ue_t *test_ue); diff --git a/tests/volte/bearer-test.c b/tests/volte/bearer-test.c index 63c5dfff7..8cbbdc688 100644 --- a/tests/volte/bearer-test.c +++ b/tests/volte/bearer-test.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2024 by Sukchan Lee * * This file is part of Open5GS. * @@ -19,7 +19,7 @@ #include "test-common.h" -static void test1_func(abts_case *tc, void *data) +static void uni_directional_func(abts_case *tc, void *data) { int rv; ogs_socknode_t *s1ap; @@ -262,11 +262,255 @@ static void test1_func(abts_case *tc, void *data) test_ue_remove(test_ue); } +static void bi_directional_func(abts_case *tc, void *data) +{ + int rv; + ogs_socknode_t *s1ap; + ogs_socknode_t *gtpu; + ogs_pkbuf_t *emmbuf; + ogs_pkbuf_t *esmbuf; + ogs_pkbuf_t *sendbuf; + ogs_pkbuf_t *recvbuf; + ogs_s1ap_message_t message; + + ogs_nas_5gs_mobile_identity_suci_t mobile_identity_suci; + test_ue_t *test_ue = NULL; + test_sess_t *sess = NULL; + test_bearer_t *bearer = NULL; + + bson_t *doc = NULL; + + /* Setup Test UE & Session Context */ + memset(&mobile_identity_suci, 0, sizeof(mobile_identity_suci)); + + mobile_identity_suci.h.supi_format = OGS_NAS_5GS_SUPI_FORMAT_IMSI; + mobile_identity_suci.h.type = OGS_NAS_5GS_MOBILE_IDENTITY_SUCI; + mobile_identity_suci.routing_indicator1 = 0; + mobile_identity_suci.routing_indicator2 = 0xf; + mobile_identity_suci.routing_indicator3 = 0xf; + mobile_identity_suci.routing_indicator4 = 0xf; + mobile_identity_suci.protection_scheme_id = OGS_PROTECTION_SCHEME_NULL; + mobile_identity_suci.home_network_pki_value = 0; + + test_ue = test_ue_add_by_suci(&mobile_identity_suci, "1032548691"); + ogs_assert(test_ue); + + test_ue->e_cgi.cell_id = 0x1079baf; + test_ue->nas.ksi = 0; + test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; + + test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; + test_ue->opc_string = "e8ed289deba952e4283b54e88e6183ca"; + + sess = test_sess_add_by_apn(test_ue, "internet", OGS_GTP2_RAT_TYPE_EUTRAN); + ogs_assert(sess); + + /* eNB connects to MME */ + s1ap = tests1ap_client(AF_INET); + ABTS_PTR_NOTNULL(tc, s1ap); + + /* eNB connects to SGW */ + gtpu = test_gtpu_server(1, AF_INET); + ABTS_PTR_NOTNULL(tc, gtpu); + + /* Send S1-Setup Reqeust */ + sendbuf = test_s1ap_build_s1_setup_request( + S1AP_ENB_ID_PR_macroENB_ID, 0x54f64); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive S1-Setup Response */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(NULL, recvbuf); + + /********** Insert Subscriber in Database */ + doc = test_db_new_qos_flow_bi_directional(test_ue); + ABTS_PTR_NOTNULL(tc, doc); + ABTS_INT_EQUAL(tc, OGS_OK, test_db_insert_ue(test_ue, doc)); + + /* Send Attach Request */ + memset(&sess->pdn_connectivity_param, + 0, sizeof(sess->pdn_connectivity_param)); + sess->pdn_connectivity_param.eit = 1; + sess->pdn_connectivity_param.request_type = + OGS_NAS_EPS_REQUEST_TYPE_INITIAL; + esmbuf = testesm_build_pdn_connectivity_request(sess, false); + ABTS_PTR_NOTNULL(tc, esmbuf); + + memset(&test_ue->attach_request_param, + 0, sizeof(test_ue->attach_request_param)); + test_ue->attach_request_param.drx_parameter = 1; + test_ue->attach_request_param.ms_network_capability = 1; + test_ue->attach_request_param.tmsi_status = 1; + test_ue->attach_request_param.mobile_station_classmark_2 = 1; + test_ue->attach_request_param.ue_usage_setting = 1; + emmbuf = testemm_build_attach_request(test_ue, esmbuf, false, false); + ABTS_PTR_NOTNULL(tc, emmbuf); + + memset(&test_ue->initial_ue_param, 0, sizeof(test_ue->initial_ue_param)); + sendbuf = test_s1ap_build_initial_ue_message( + test_ue, emmbuf, S1AP_RRC_Establishment_Cause_mo_Signalling, false); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Authentication Request */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send Authentication response */ + emmbuf = testemm_build_authentication_response(test_ue); + ABTS_PTR_NOTNULL(tc, emmbuf); + sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, emmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Security mode Command */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send Security mode complete */ + test_ue->mobile_identity_imeisv_presence = true; + emmbuf = testemm_build_security_mode_complete(test_ue); + ABTS_PTR_NOTNULL(tc, emmbuf); + sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, emmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive ESM Information Request */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send ESM Information Response */ + sess->esm_information_param.pco = 1; + esmbuf = testesm_build_esm_information_response(sess); + ABTS_PTR_NOTNULL(tc, esmbuf); + sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, esmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Initial Context Setup Request + + * Attach Accept + + * Activate Default Bearer Context Request */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send UE Capability Info Indication */ + sendbuf = tests1ap_build_ue_radio_capability_info_indication(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send Initial Context Setup Response */ + sendbuf = test_s1ap_build_initial_context_setup_response(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send Attach Complete + Activate default EPS bearer cotext accept */ + test_ue->nr_cgi.cell_id = 0x1234502; + bearer = test_bearer_find_by_ue_ebi(test_ue, 5); + ogs_assert(bearer); + esmbuf = testesm_build_activate_default_eps_bearer_context_accept( + bearer, false); + ABTS_PTR_NOTNULL(tc, esmbuf); + emmbuf = testemm_build_attach_complete(test_ue, esmbuf); + ABTS_PTR_NOTNULL(tc, emmbuf); + sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, emmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive EMM information */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Receive E-RABSetupRequest + + * Activate dedicated EPS bearer context request */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + S1AP_ProcedureCode_id_E_RABSetup, + test_ue->s1ap_procedure_code); + + /* Send E-RABSetupResponse */ + bearer = test_bearer_find_by_ue_ebi(test_ue, 6); + ogs_assert(bearer); + sendbuf = test_s1ap_build_e_rab_setup_response(bearer); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send Activate dedicated EPS bearer context accept */ + esmbuf = testesm_build_activate_dedicated_eps_bearer_context_accept(bearer); + ABTS_PTR_NOTNULL(tc, esmbuf); + sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, esmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + ogs_msleep(100); + + /* Send GTP-U ICMP Packet */ + bearer = test_bearer_find_by_ue_ebi(test_ue, 6); + ogs_assert(bearer); + rv = test_gtpu_send_ping(gtpu, bearer, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = test_gtpu_read(gtpu); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send UE Context Release Request */ + sendbuf = test_s1ap_build_ue_context_release_request(test_ue, + S1AP_Cause_PR_radioNetwork, S1AP_CauseRadioNetwork_user_inactivity); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive UE Context Release Command */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send UE Context Release Complete */ + sendbuf = test_s1ap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + ogs_msleep(300); + + /********** Remove Subscriber in Database */ + ABTS_INT_EQUAL(tc, OGS_OK, test_db_remove_ue(test_ue)); + + /* eNB disonncect from MME */ + testenb_s1ap_close(s1ap); + + /* eNB disonncect from SGW */ + test_gtpu_close(gtpu); + + test_ue_remove(test_ue); +} + abts_suite *test_bearer(abts_suite *suite) { suite = ADD_SUITE(suite) - abts_run_test(suite, test1_func, NULL); + abts_run_test(suite, uni_directional_func, NULL); + abts_run_test(suite, bi_directional_func, NULL); return suite; } diff --git a/tests/vonr/qos-flow-test.c b/tests/vonr/qos-flow-test.c index f003c0e95..087c28703 100644 --- a/tests/vonr/qos-flow-test.c +++ b/tests/vonr/qos-flow-test.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019,2020 by Sukchan Lee + * Copyright (C) 2019-2024 by Sukchan Lee * * This file is part of Open5GS. * @@ -19,7 +19,7 @@ #include "test-common.h" -static void test1_func(abts_case *tc, void *data) +static void uni_directional_func(abts_case *tc, void *data) { int rv; ogs_socknode_t *ngap; @@ -411,11 +411,398 @@ static void test1_func(abts_case *tc, void *data) test_ue_remove(test_ue); } +static void bi_directional_func(abts_case *tc, void *data) +{ + int rv; + ogs_socknode_t *ngap; + ogs_socknode_t *gtpu; + ogs_pkbuf_t *gmmbuf; + ogs_pkbuf_t *gsmbuf; + ogs_pkbuf_t *nasbuf; + ogs_pkbuf_t *sendbuf; + ogs_pkbuf_t *recvbuf; + ogs_ngap_message_t message; + int i; + + uint8_t tmp[OGS_HUGE_LEN]; + char *_gtp_payload = "34ff0024" + "0000000100000085 010002004500001c 0c0b000040015a7a 0a2d00010a2d0002" + "00000964cd7c291f"; + + ogs_nas_5gs_mobile_identity_suci_t mobile_identity_suci; + test_ue_t *test_ue = NULL; + test_sess_t *sess = NULL; + test_bearer_t *qos_flow = NULL; + + bson_t *doc = NULL; + + /* Setup Test UE & Session Context */ + memset(&mobile_identity_suci, 0, sizeof(mobile_identity_suci)); + + mobile_identity_suci.h.supi_format = OGS_NAS_5GS_SUPI_FORMAT_IMSI; + mobile_identity_suci.h.type = OGS_NAS_5GS_MOBILE_IDENTITY_SUCI; + mobile_identity_suci.routing_indicator1 = 0; + mobile_identity_suci.routing_indicator2 = 0xf; + mobile_identity_suci.routing_indicator3 = 0xf; + mobile_identity_suci.routing_indicator4 = 0xf; + mobile_identity_suci.protection_scheme_id = OGS_PROTECTION_SCHEME_NULL; + mobile_identity_suci.home_network_pki_value = 0; + + test_ue = test_ue_add_by_suci(&mobile_identity_suci, "0000203190"); + ogs_assert(test_ue); + + test_ue->nr_cgi.cell_id = 0x40001; + + test_ue->nas.registration.tsc = 0; + test_ue->nas.registration.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; + test_ue->nas.registration.follow_on_request = 1; + test_ue->nas.registration.value = OGS_NAS_5GS_REGISTRATION_TYPE_INITIAL; + + test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; + test_ue->opc_string = "e8ed289deba952e4283b54e88e6183ca"; + + /* gNB connects to AMF */ + ngap = testngap_client(AF_INET); + ABTS_PTR_NOTNULL(tc, ngap); + + /* gNB connects to UPF */ + gtpu = test_gtpu_server(1, AF_INET); + ABTS_PTR_NOTNULL(tc, gtpu); + + /* Send NG-Setup Reqeust */ + sendbuf = testngap_build_ng_setup_request(0x4000, 22); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive NG-Setup Response */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /********** Insert Subscriber in Database */ + doc = test_db_new_qos_flow_bi_directional(test_ue); + ABTS_PTR_NOTNULL(tc, doc); + ABTS_INT_EQUAL(tc, OGS_OK, test_db_insert_ue(test_ue, doc)); + + /* Send Registration request */ + test_ue->registration_request_param.guti = 1; + gmmbuf = testgmm_build_registration_request(test_ue, NULL, false, false); + ABTS_PTR_NOTNULL(tc, gmmbuf); + + test_ue->registration_request_param.gmm_capability = 1; + test_ue->registration_request_param.requested_nssai = 1; + test_ue->registration_request_param.last_visited_registered_tai = 1; + test_ue->registration_request_param.ue_usage_setting = 1; + nasbuf = testgmm_build_registration_request(test_ue, NULL, false, false); + ABTS_PTR_NOTNULL(tc, nasbuf); + + sendbuf = testngap_build_initial_ue_message(test_ue, gmmbuf, + NGAP_RRCEstablishmentCause_mo_Signalling, false, true); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Identity request */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send Identity response */ + gmmbuf = testgmm_build_identity_response(test_ue); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Authentication request */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send Authentication response */ + gmmbuf = testgmm_build_authentication_response(test_ue); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Security mode command */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send Security mode complete */ + gmmbuf = testgmm_build_security_mode_complete(test_ue, nasbuf); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive InitialContextSetupRequest + + * Registration accept */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_InitialContextSetup, + test_ue->ngap_procedure_code); + + /* Send UERadioCapabilityInfoIndication */ + sendbuf = testngap_build_ue_radio_capability_info_indication(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send InitialContextSetupResponse */ + sendbuf = testngap_build_initial_context_setup_response(test_ue, false); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send Registration complete */ + gmmbuf = testgmm_build_registration_complete(test_ue); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Configuration update command */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send PDU session establishment request */ + sess = test_sess_add_by_dnn_and_psi(test_ue, "internet", 5); + ogs_assert(sess); + + sess->ul_nas_transport_param.request_type = + OGS_NAS_5GS_REQUEST_TYPE_INITIAL; + sess->ul_nas_transport_param.dnn = 1; + sess->ul_nas_transport_param.s_nssai = 1; + + sess->pdu_session_establishment_param.ssc_mode = 1; + sess->pdu_session_establishment_param.epco = 1; + + gsmbuf = testgsm_build_pdu_session_establishment_request(sess); + ABTS_PTR_NOTNULL(tc, gsmbuf); + gmmbuf = testgmm_build_ul_nas_transport(sess, + OGS_NAS_PAYLOAD_CONTAINER_N1_SM_INFORMATION, gsmbuf); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive PDUSessionResourceSetupRequest + + * DL NAS transport + + * PDU session establishment accept */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_PDUSessionResourceSetup, + test_ue->ngap_procedure_code); + + /* Send GTP-U ICMP Packet */ + qos_flow = test_qos_flow_find_by_qfi(sess, 1); + ogs_assert(qos_flow); + rv = test_gtpu_send_ping(gtpu, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send PDUSessionResourceSetupResponse */ + sendbuf = testngap_sess_build_pdu_session_resource_setup_response(sess); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send GTP-U ICMP Packet */ + rv = test_gtpu_send_ping(gtpu, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Receive PDUSessionResourceModifyRequest + + * DL NAS transport + + * PDU session modification command */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_PDUSessionResourceModify, + test_ue->ngap_procedure_code); + + /* Send PDU session resource modify response */ + qos_flow = test_qos_flow_find_by_qfi(sess, 2); + ogs_assert(qos_flow); + + sendbuf = testngap_build_qos_flow_resource_modify_response(qos_flow); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send PDU session resource modify complete */ + sess->ul_nas_transport_param.request_type = + OGS_NAS_5GS_REQUEST_TYPE_MODIFICATION_REQUEST; + sess->ul_nas_transport_param.dnn = 0; + sess->ul_nas_transport_param.s_nssai = 0; + + sess->pdu_session_establishment_param.ssc_mode = 0; + sess->pdu_session_establishment_param.epco = 0; + + gsmbuf = testgsm_build_pdu_session_modification_complete(sess); + ABTS_PTR_NOTNULL(tc, gsmbuf); + gmmbuf = testgmm_build_ul_nas_transport(sess, + OGS_NAS_PAYLOAD_CONTAINER_N1_SM_INFORMATION, gsmbuf); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Waiting for creating dedicated QoS flow in PFCP protocol */ + ogs_msleep(100); + + /* Send GTP-U ICMP Packet */ + rv = test_gtpu_send_ping(gtpu, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send UEContextReleaseRequest */ + sendbuf = testngap_build_ue_context_release_request(test_ue, + NGAP_Cause_PR_radioNetwork, NGAP_CauseRadioNetwork_user_inactivity, + true); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive UEContextReleaseCommand */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_UEContextRelease, + test_ue->ngap_procedure_code); + + /* Send UEContextReleaseComplete */ + sendbuf = testngap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* + * Send InitialUEMessage + + * Service request + * - Uplink Data Status + */ + test_ue->service_request_param.uplink_data_status = 1; + test_ue->service_request_param.psimask.uplink_data_status = 1 << sess->psi; + test_ue->service_request_param.pdu_session_status = 0; + nasbuf = testgmm_build_service_request( + test_ue, OGS_NAS_SERVICE_TYPE_DATA, NULL, false, false); + ABTS_PTR_NOTNULL(tc, nasbuf); + + test_ue->service_request_param.uplink_data_status = 0; + test_ue->service_request_param.pdu_session_status = 0; + gmmbuf = testgmm_build_service_request( + test_ue, OGS_NAS_SERVICE_TYPE_DATA, nasbuf, true, false); + ABTS_PTR_NOTNULL(tc, gmmbuf); + + sendbuf = testngap_build_initial_ue_message(test_ue, gmmbuf, + NGAP_RRCEstablishmentCause_mo_Signalling, true, true); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive InitialContextSetupRequest + + * Service accept */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_InitialContextSetup, + test_ue->ngap_procedure_code); + ABTS_INT_EQUAL(tc, 0x0000, test_ue->pdu_session_status); + ABTS_INT_EQUAL(tc, 0x0000, test_ue->pdu_session_reactivation_result); + + /* Send InitialContextSetupResponse */ + sendbuf = testngap_build_initial_context_setup_response(test_ue, true); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Wait to setup N3 data connection. + * Otherwise, network-triggered service request is initiated */ + ogs_msleep(100); + + /* Send GTP-U ICMP Packet */ + rv = test_gtpu_send_ping(gtpu, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send De-registration request */ + gmmbuf = testgmm_build_de_registration_request(test_ue, 1, true, true); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive UEContextReleaseCommand */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_UEContextRelease, + test_ue->ngap_procedure_code); + + /* Send UEContextReleaseComplete */ + sendbuf = testngap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + ogs_msleep(300); + + /********** Remove Subscriber in Database */ + ABTS_INT_EQUAL(tc, OGS_OK, test_db_remove_ue(test_ue)); + + /* gNB disonncect from UPF */ + testgnb_gtpu_close(gtpu); + + /* gNB disonncect from AMF */ + testgnb_ngap_close(ngap); + + /* Clear Test UE Context */ + test_ue_remove(test_ue); +} + abts_suite *test_qos_flow(abts_suite *suite) { suite = ADD_SUITE(suite) - abts_run_test(suite, test1_func, NULL); + abts_run_test(suite, uni_directional_func, NULL); + abts_run_test(suite, bi_directional_func, NULL); return suite; }