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; }