diff --git a/lib/gtp/v1/types.h b/lib/gtp/v1/types.h index ed82486b8..b3a4c8af5 100644 --- a/lib/gtp/v1/types.h +++ b/lib/gtp/v1/types.h @@ -277,6 +277,18 @@ typedef struct ogs_gtp1_qos_profile_decoded_s { #define OGS_GTP1_QOS_SRC_STATS_DESC_UNKNOWN 0 #define OGS_GTP1_QOS_SRC_STATS_DESC_SPEECH 1 +/* 7.7.48 Common Flags */ +typedef struct ogs_gtp1_common_flags_s { +ED8(uint8_t dual_address_bearer_flag:1;, + uint8_t upgrade_qos_supported:1;, + uint8_t nrsn:1;, + uint8_t no_qos_negotiation:1;, + uint8_t mbms_counting_information:1;, + uint8_t ran_procedures_ready:1;, + uint8_t mbms_service_type:1;, + uint8_t prohibit_payload_compression:1;) +} __attribute__ ((packed)) ogs_gtp1_common_flags_t; + /* 7.7.98 APN Aggregate Maximum Bit Rate (APN-AMBR) */ typedef struct ogs_gtp1_apn_ambr_s { uint32_t uplink; diff --git a/src/smf/context.c b/src/smf/context.c index 31667a357..510d746b0 100644 --- a/src/smf/context.c +++ b/src/smf/context.c @@ -1606,6 +1606,7 @@ void smf_sess_remove(smf_sess_t *sess) OGS_TLV_CLEAR_DATA(&sess->gtp.user_location_information); OGS_TLV_CLEAR_DATA(&sess->gtp.ue_timezone); OGS_TLV_CLEAR_DATA(&sess->gtp.charging_characteristics); + OGS_TLV_CLEAR_DATA(&sess->gtp.v1.qos); OGS_NAS_CLEAR_DATA(&sess->nas.ue_pco); diff --git a/src/smf/context.h b/src/smf/context.h index 80563759f..5d89e0175 100644 --- a/src/smf/context.h +++ b/src/smf/context.h @@ -333,6 +333,9 @@ typedef struct smf_sess_s { uint8_t selection_mode; /* OGS_GTP{1,2}_SELECTION_MODE_*, same in GTPv1C and 2C. */ struct { uint8_t nsapi; + ogs_gtp1_common_flags_t common_flags; + ogs_tlv_octet_t qos; /* Encoded GTPv1C "QoS Profile" IE */ + ogs_gtp1_qos_profile_decoded_t qos_pdec; bool peer_supports_apn_ambr; } v1; /* GTPv1C specific fields */ } gtp; /* Saved from S5-C/Gn */ diff --git a/src/smf/gn-build.c b/src/smf/gn-build.c index 3eb025e1e..1ef0c9e6a 100644 --- a/src/smf/gn-build.c +++ b/src/smf/gn-build.c @@ -27,19 +27,10 @@ static void build_qos_profile_from_session(ogs_gtp1_qos_profile_decoded_t *qos_pdec, const smf_sess_t *sess, const smf_bearer_t *bearer) { - memset(qos_pdec, 0, sizeof(*qos_pdec)); + /* Initialize with defaults retrieved from MS/SGSN: */ + memcpy(qos_pdec, &sess->gtp.v1.qos_pdec, sizeof(*qos_pdec)); qos_pdec->qos_profile.arp = sess->session.qos.arp.priority_level; - qos_pdec->qos_profile.data.reliability_class = 3; /* Unacknowledged GTP and LLC; Acknowledged RLC, Protected data */ - qos_pdec->qos_profile.data.precedence_class = 2; /* Normal priority */ - qos_pdec->qos_profile.data.peak_throughput = 9; /* Up to 256 000 octet/s */ - qos_pdec->qos_profile.data.mean_throughput = 0x1f; /* Best effort */ - qos_pdec->qos_profile.data.delivery_erroneous_sdu = 2; /* Erroneous SDUs are delivered ('yes') */ - qos_pdec->qos_profile.data.delivery_order = 2; /* Without delivery order ('no') */ - qos_pdec->qos_profile.data.max_sdu_size = 0x96; /* 1500 octets */ - qos_pdec->qos_profile.data.residual_ber = 5; /* 1*10^-4, <= 2*10^-4 */ - qos_pdec->qos_profile.data.sdu_error_ratio = 4; /* 1*10^-4 */ - /* 3GPP TS 23.401 Annex E table Table E.3 */ /* Also take into account table 7 in 3GPP TS 23.107 9.1.2.2 */ @@ -102,6 +93,22 @@ static void build_qos_profile_from_session(ogs_gtp1_qos_profile_decoded_t *qos_p qos_pdec->dec_mbr_kbps_ul = sess->session.ambr.uplink / 1000; qos_pdec->dec_gbr_kbps_dl = bearer->qos.gbr.downlink / 1000; qos_pdec->dec_gbr_kbps_ul = bearer->qos.gbr.uplink / 1000; + + /* Don't upgrade values if Common Flags "Upgrade QoS Supported" is 0: */ + if (!sess->gtp.v1.common_flags.upgrade_qos_supported) { + if (sess->gtp.v1.qos_pdec.dec_mbr_kbps_dl > 0) + qos_pdec->dec_mbr_kbps_dl = ogs_min(qos_pdec->dec_mbr_kbps_dl, + sess->gtp.v1.qos_pdec.dec_mbr_kbps_dl); + if (sess->gtp.v1.qos_pdec.dec_mbr_kbps_ul > 0) + qos_pdec->dec_mbr_kbps_ul = ogs_min(qos_pdec->dec_mbr_kbps_ul, + sess->gtp.v1.qos_pdec.dec_mbr_kbps_ul); + if (sess->gtp.v1.qos_pdec.dec_gbr_kbps_dl > 0) + qos_pdec->dec_gbr_kbps_dl = ogs_min(qos_pdec->dec_gbr_kbps_dl, + sess->gtp.v1.qos_pdec.dec_gbr_kbps_dl); + if (sess->gtp.v1.qos_pdec.dec_gbr_kbps_ul > 0) + qos_pdec->dec_gbr_kbps_ul = ogs_min(qos_pdec->dec_gbr_kbps_ul, + sess->gtp.v1.qos_pdec.dec_gbr_kbps_ul); + } } ogs_pkbuf_t *smf_gn_build_create_pdp_context_response( @@ -246,12 +253,16 @@ ogs_pkbuf_t *smf_gn_build_create_pdp_context_response( rsp->ggsn_address_for_user_traffic.data = &pgw_gnu_gsnaddr; rsp->ggsn_address_for_user_traffic.len = gsn_len; - /* QoS Profile: if PCRF changes Bearer QoS, this should be included. */ + /* QoS Profile: if PCRF changes Bearer QoS, apply changes. */ if (sess->gtp.create_session_response_bearer_qos == true) { build_qos_profile_from_session(&qos_pdec, sess, bearer); rsp->quality_of_service_profile.presence = 1; ogs_gtp1_build_qos_profile(&rsp->quality_of_service_profile, &qos_pdec, qos_pdec_buf, OGS_GTP1_QOS_PROFILE_MAX_LEN); + } else { + /* Copy over received QoS Profile from originating Request: */ + memcpy(&rsp->quality_of_service_profile, &sess->gtp.v1.qos, + sizeof(rsp->quality_of_service_profile)); } /* TODO: Charging Gateway Address */ @@ -365,9 +376,14 @@ ogs_pkbuf_t *smf_gn_build_update_pdp_context_response( rsp->charging_id.presence = 1; rsp->charging_id.u32 = sess->charging.id; - /* Protocol Configuration Options (PCO) */ - if (sess->gtp.ue_pco.presence && - sess->gtp.ue_pco.len && sess->gtp.ue_pco.data) { + /* Protocol Configuration Options (PCO): + * If the "No QoS negotiation" bit of the Common Flags IE in the Update PDP + * Context Request message was set to 1, then the GGSN [...] shall not + * include the Protocol Configuration Options (PCO) information element in + * the message) */ + if (!sess->gtp.v1.common_flags.no_qos_negotiation && + sess->gtp.ue_pco.presence && + sess->gtp.ue_pco.len && sess->gtp.ue_pco.data) { pco_len = smf_pco_build( pco_buf, sess->gtp.ue_pco.data, sess->gtp.ue_pco.len); ogs_assert(pco_len > 0); @@ -437,12 +453,18 @@ ogs_pkbuf_t *smf_gn_build_update_pdp_context_response( rsp->ggsn_address_for_user_traffic.data = &pgw_gnu_gsnaddr; rsp->ggsn_address_for_user_traffic.len = gsn_len; - /* QoS Profile: if PCRF changes Bearer QoS, this should be included. */ - if (sess->gtp.create_session_response_bearer_qos == true) { + /* QoS Profile: if SGSN supports QoS re-negotiation and PCRF changes Bearer + * QoS, apply changes: */ + if (!sess->gtp.v1.common_flags.no_qos_negotiation && + sess->gtp.create_session_response_bearer_qos == true) { build_qos_profile_from_session(&qos_pdec, sess, bearer); rsp->quality_of_service_profile.presence = 1; ogs_gtp1_build_qos_profile(&rsp->quality_of_service_profile, &qos_pdec, qos_pdec_buf, OGS_GTP1_QOS_PROFILE_MAX_LEN); + } else { + /* Copy over received QoS Profile from originating Request: */ + memcpy(&rsp->quality_of_service_profile, &sess->gtp.v1.qos, + sizeof(rsp->quality_of_service_profile)); } /* TODO: Charging Gateway Address */ diff --git a/src/smf/gn-handler.c b/src/smf/gn-handler.c index 225dedab8..3014085b5 100644 --- a/src/smf/gn-handler.c +++ b/src/smf/gn-handler.c @@ -62,7 +62,7 @@ uint8_t smf_gn_handle_create_pdp_context_request( smf_ue_t *smf_ue = NULL; ogs_eua_t *eua = NULL; smf_bearer_t *bearer = NULL; - ogs_gtp1_qos_profile_decoded_t qos_pdec; + ogs_gtp1_qos_profile_decoded_t *qos_pdec; uint8_t qci = 9; ogs_assert(sess); @@ -178,27 +178,33 @@ uint8_t smf_gn_handle_create_pdp_context_request( smf_ue->msisdn, smf_ue->msisdn_len, smf_ue->msisdn_bcd); } - /* Set some sane default if infomation not present in Qos Profile or APN-AMBR: */ + /* Common Flags 7.7.48 */ + if (req->common_flags.presence) { + sess->gtp.v1.common_flags = *(ogs_gtp1_common_flags_t*)req->common_flags.data; + } + + /* Set some sane default if information not present in QoS Profile or APN-AMBR: */ sess->session.ambr.downlink = 102400000; sess->session.ambr.uplink = 102400000; /* Set Bearer QoS */ - rv = ogs_gtp1_parse_qos_profile(&qos_pdec, - &req->quality_of_service_profile); + OGS_TLV_STORE_DATA(&sess->gtp.v1.qos, &req->quality_of_service_profile); + qos_pdec = &sess->gtp.v1.qos_pdec; + rv = ogs_gtp1_parse_qos_profile(qos_pdec, &req->quality_of_service_profile); if(rv < 0) return OGS_GTP1_CAUSE_MANDATORY_IE_INCORRECT; /* 3GPP TS 23.060 section 9.2.1A: "The QoS profiles of the PDP context and EPS bearer are mapped as specified in TS 23.401" * 3GPP TS 23.401 Annex E: "Mapping between EPS and Release 99 QoS parameters" */ - ogs_gtp1_qos_profile_to_qci(&qos_pdec, &qci); + ogs_gtp1_qos_profile_to_qci(qos_pdec, &qci); sess->session.qos.index = qci; - sess->session.qos.arp.priority_level = qos_pdec.qos_profile.arp; /* 3GPP TS 23.401 Annex E Table E.2 */ + sess->session.qos.arp.priority_level = qos_pdec->qos_profile.arp; /* 3GPP TS 23.401 Annex E Table E.2 */ sess->session.qos.arp.pre_emption_capability = 0; /* ignored as per 3GPP TS 23.401 Annex E */ sess->session.qos.arp.pre_emption_vulnerability = 0; /* ignored as per 3GPP TS 23.401 Annex E */ - if (qos_pdec.data_octet6_to_13_present) { - sess->session.ambr.downlink = qos_pdec.dec_mbr_kbps_dl * 1000; - sess->session.ambr.uplink = qos_pdec.dec_mbr_kbps_ul * 1000; + if (qos_pdec->data_octet6_to_13_present) { + sess->session.ambr.downlink = qos_pdec->dec_mbr_kbps_dl * 1000; + sess->session.ambr.uplink = qos_pdec->dec_mbr_kbps_ul * 1000; } /* APN-AMBR, 7.7.98 */ @@ -266,9 +272,9 @@ uint8_t smf_gn_handle_create_pdp_context_request( ogs_assert(rv == OGS_OK); ogs_debug(" SGW_S5U_TEID[0x%x] PGW_S5U_TEID[0x%x]", bearer->sgw_s5u_teid, bearer->pgw_s5u_teid); - if (qos_pdec.data_octet6_to_13_present) { - bearer->qos.gbr.downlink = qos_pdec.dec_gbr_kbps_dl * 1000; - bearer->qos.gbr.uplink = qos_pdec.dec_gbr_kbps_ul * 1000; + if (qos_pdec->data_octet6_to_13_present) { + bearer->qos.gbr.downlink = qos_pdec->dec_gbr_kbps_dl * 1000; + bearer->qos.gbr.uplink = qos_pdec->dec_gbr_kbps_ul * 1000; } else { /* Set some sane default if infomation not present in Qos Profile IE: */ bearer->qos.gbr.downlink = sess->session.ambr.downlink; @@ -324,6 +330,8 @@ void smf_gn_handle_update_pdp_context_request( ogs_pfcp_pdr_t *pdr = NULL; smf_bearer_t *bearer = NULL; smf_ue_t *smf_ue = NULL; + ogs_gtp1_qos_profile_decoded_t *qos_pdec; + uint8_t qci; ogs_debug("Update PDP Context Request"); @@ -382,6 +390,14 @@ void smf_gn_handle_update_pdp_context_request( } } + /* Common Flags 7.7.48 */ + if (req->common_flags.presence) { + sess->gtp.v1.common_flags = *(ogs_gtp1_common_flags_t*)req->common_flags.data; + } else { + /* Reset it to overwrite what was received during CreatePDPCtxReq time */ + sess->gtp.v1.common_flags = (ogs_gtp1_common_flags_t){0}; + } + /* Control Plane(DL) : SGW-S5C */ if (req->tunnel_endpoint_identifier_control_plane.presence) { sess->sgw_s5c_teid = req->tunnel_endpoint_identifier_control_plane.u32; @@ -402,6 +418,50 @@ void smf_gn_handle_update_pdp_context_request( ogs_debug(" Updated SGW_S5U_TEID[0x%x] PGW_S5U_TEID[0x%x]", bearer->sgw_s5u_teid, bearer->pgw_s5u_teid); + + /* Set Bearer QoS */ + OGS_TLV_STORE_DATA(&sess->gtp.v1.qos, &req->quality_of_service_profile); + qos_pdec = &sess->gtp.v1.qos_pdec; + rv = ogs_gtp1_parse_qos_profile(qos_pdec, &req->quality_of_service_profile); + if(rv < 0) { + ogs_gtp1_send_error_message(xact, sess->sgw_s5c_teid, + OGS_GTP1_UPDATE_PDP_CONTEXT_RESPONSE_TYPE, + OGS_GTP1_CAUSE_MANDATORY_IE_INCORRECT); + return; + } + + /* 3GPP TS 23.060 section 9.2.1A: "The QoS profiles of the PDP context and EPS bearer are mapped as specified in TS 23.401" + * 3GPP TS 23.401 Annex E: "Mapping between EPS and Release 99 QoS parameters" + */ + ogs_gtp1_qos_profile_to_qci(qos_pdec, &qci); + sess->session.qos.index = qci; + sess->session.qos.arp.priority_level = qos_pdec->qos_profile.arp; /* 3GPP TS 23.401 Annex E Table E.2 */ + sess->session.qos.arp.pre_emption_capability = 0; /* ignored as per 3GPP TS 23.401 Annex E */ + sess->session.qos.arp.pre_emption_vulnerability = 0; /* ignored as per 3GPP TS 23.401 Annex E */ + if (qos_pdec->data_octet6_to_13_present) { + sess->session.ambr.downlink = qos_pdec->dec_mbr_kbps_dl * 1000; + sess->session.ambr.uplink = qos_pdec->dec_mbr_kbps_ul * 1000; + } + + /* APN-AMBR, 7.7.98 */ + if (req->apn_ambr.presence) { + /* "The APN-AMBR IE shall be included as the authorized APN-AMBR if the + * GGSN supports this IE and if the APN-AMBR IE has been included in the + * corresponding request message." */ + sess->gtp.v1.peer_supports_apn_ambr = true; + if (req->apn_ambr.len >= sizeof(ogs_gtp1_apn_ambr_t)) { + ogs_gtp1_apn_ambr_t *ambr = req->apn_ambr.data; + sess->session.ambr.uplink = be32toh(ambr->uplink) * 1000; + sess->session.ambr.downlink = be32toh(ambr->downlink) * 1000; + } + } + + /* PCO */ + if (req->protocol_configuration_options.presence) { + OGS_TLV_STORE_DATA(&sess->gtp.ue_pco, + &req->protocol_configuration_options); + } + memset(&h, 0, sizeof(ogs_gtp2_header_t)); h.type = OGS_GTP1_UPDATE_PDP_CONTEXT_RESPONSE_TYPE; h.teid = sess->sgw_s5c_teid;