Compare commits

...

6 Commits

Author SHA1 Message Date
Oliver Smith 7579f351b6 d/changelog: add 2.6.6~sysmocom3 2024-01-29 14:22:06 +01:00
Pau Espin e45eca76b6 [SMF,MME] Gn: Set Maximum SDU Size QoS field to 1500
Before this patch, it was set as 0, which is Reserved in Network to MS
direction.

(cherry picked from commit 88a77f7bc5)
2024-01-29 14:18:03 +01:00
Pau Espin e2b747546b [SMF,MME] Gn: Set Delivery of erroneous SDUs QoS field to No
Before this patch, it was set as 0, which is Reserved in Network to MS
direction.

(cherry picked from commit a613be8c4c)
2024-01-29 14:17:57 +01:00
Pau Espin 70210324f2 [SMF,MME] Gn: Set Delivery order QoS field to No
Before this patch, it was set as 0, which is Reserved in Network to MS
direction.

(cherry picked from commit d95c82b21c)
2024-01-29 14:17:52 +01:00
Pau Espin e84450bfc1 [MME] Gn: Introduce initial support for 4G->2G cell reselection
In an Inter-RAT setup a UE could perform a RAU coming from a 4G network.
In that case the UE/MS is unknown to the SGSN and it should request the
SGSN context (MM, PDP) from the MME. This is done through the following
GTPv1C message exchange on the Gn interface of SGSN and MME:
SGSN -> MME: SGSN Context Request
SGSN <- MME: SGSN Context Response
SGSN -> MME: SGSN Context Acknowledge

This commit doesn't aim to be a complete implementation of the mentioned
procedure, since it's quite a complex one, with lots of fields and logic
required. This so far only implements in general the minimally
successful case by filling as much as possible the required set of
fields.
This will allow for a base onto which do incremental improvements and
fixes while testing against UEs and SGSNs (such as osmo-sgsn, which
doesn't yet support this procedure but will potentially earn it soon).

This commit doesn't implement the reverse direction, aka UE issuing cell
reselection 2G->4G. Initial support for this scenario will hopefully be
added soon as a follow-up patch, similar to this one.

Related: https://osmocom.org/issues/6294
(cherry picked from commit 3d693da73e)
2024-01-29 14:17:39 +01:00
Pau Espin 4e08baa633 [MME] Split Gn local TEID generation from S11 TEID generation
(cherry picked from commit 14932a7254)
2024-01-29 14:17:30 +01:00
23 changed files with 1177 additions and 16 deletions

14
debian/changelog vendored
View File

@ -1,3 +1,17 @@
open5gs (2.6.6~sysmocom3) unstable; urgency=medium
Add patches from https://github.com/open5gs/open5gs/pull/2921 with
dependencies (SYS#6582):
[ Pau Espin Pedrol ]
* [MME] Split Gn local TEID generation from S11 TEID generation
* [MME] Gn: Introduce initial support for 4G->2G cell reselection
* [SMF,MME] Gn: Set Delivery order QoS field to No
* [SMF,MME] Gn: Set Delivery of erroneous SDUs QoS field to No
* [SMF,MME] Gn: Set Maximum SDU Size QoS field to 1500
-- Oliver Smith <osmith@sysmocom.de> Mon, 29 Jan 2024 14:19:52 +0100
open5gs (2.6.6~sysmocom2) unstable; urgency=medium
* Fix fixed-0 IPCP identifier in PCO ack

View File

@ -34,6 +34,9 @@
#define FC_FOR_KENB_DERIVATION 0x11
#define FC_FOR_NH_ENB_DERIVATION 0x12
#define FC_FOR_EPS_ALGORITHM_KEY_DERIVATION 0x15
#define FC_FOR_CK_IK_DERIVATION_HANDOVER 0x16
#define FC_FOR_NAS_TOKEN_DERIVATION 0x17
#define FC_FOR_CK_IK_DERIVATION_IDLE_MOBILITY 0x1B
typedef struct kdf_param_s {
uint8_t *buf;
@ -377,6 +380,56 @@ void ogs_kdf_nas_eps(uint8_t algorithm_type_distinguishers,
memcpy(knas, output+16, 16);
}
/* TS33.401 Annex A.8: KASME to CK', IK' derivation at handover */
void ogs_kdf_ck_ik_handover(
uint32_t dl_count, const uint8_t *kasme, uint8_t *ck, uint8_t *ik)
{
kdf_param_t param;
uint8_t output[OGS_SHA256_DIGEST_SIZE];
memset(param, 0, sizeof(param));
param[0].buf = (uint8_t *)&dl_count;
param[0].len = 4;
ogs_kdf_common(kasme, OGS_SHA256_DIGEST_SIZE,
FC_FOR_CK_IK_DERIVATION_HANDOVER, param, output);
memcpy(ck, output, 16);
memcpy(ik, output+16, 16);
}
/* TS33.401 Annex A.9: NAS token derivation for inter-RAT mobility */
void ogs_kdf_nas_token(
uint32_t ul_count, const uint8_t *kasme, uint8_t *nas_token)
{
kdf_param_t param;
uint8_t output[OGS_SHA256_DIGEST_SIZE];
memset(param, 0, sizeof(param));
param[0].buf = (uint8_t *)&ul_count;
param[0].len = 4;
ogs_kdf_common(kasme, OGS_SHA256_DIGEST_SIZE,
FC_FOR_NAS_TOKEN_DERIVATION, param, output);
memcpy(nas_token, output, 2);
}
/* TS33.401 Annex A.13: KASME to CK', IK' derivation at idle mobility */
void ogs_kdf_ck_ik_idle_mobility(
uint32_t ul_count, const uint8_t *kasme, uint8_t *ck, uint8_t *ik)
{
kdf_param_t param;
uint8_t output[OGS_SHA256_DIGEST_SIZE];
memset(param, 0, sizeof(param));
param[0].buf = (uint8_t *)&ul_count;
param[0].len = 4;
ogs_kdf_common(kasme, OGS_SHA256_DIGEST_SIZE,
FC_FOR_CK_IK_DERIVATION_IDLE_MOBILITY, param, output);
memcpy(ck, output, 16);
memcpy(ik, output+16, 16);
}
/*
* TS33.401 Annex I Hash Functions
* Use the KDF given in TS33.220

View File

@ -101,6 +101,18 @@ void ogs_kdf_nh_enb(uint8_t *kasme, uint8_t *sync_input, uint8_t *kenb);
void ogs_kdf_nas_eps(uint8_t algorithm_type_distinguishers,
uint8_t algorithm_identity, uint8_t *kasme, uint8_t *knas);
/* TS33.401 Annex A.8: KASME to CK', IK' derivation at handover */
void ogs_kdf_ck_ik_handover(
uint32_t dl_count, const uint8_t *kasme, uint8_t *ck, uint8_t *ik);
/* TS33.401 Annex A.9: NAS token derivation for inter-RAT mobility */
void ogs_kdf_nas_token(
uint32_t ul_count, const uint8_t *kasme, uint8_t *nas_token);
/* TS33.401 Annex A.13: KASME to CK', IK' derivation at idle mobility */
void ogs_kdf_ck_ik_idle_mobility(
uint32_t ul_count, const uint8_t *kasme, uint8_t *ck, uint8_t *ik);
/*
* TS33.401 Annex I Hash Functions
* Use the KDF given in TS33.220

View File

@ -104,6 +104,34 @@ int ogs_gtp1_gsn_addr_to_ip(const ogs_gtp1_gsn_addr_t *gsnaddr, uint16_t gsnaddr
return OGS_OK;
}
int ogs_gtp1_pdu_session_type_to_eua_ietf_type(uint8_t session_type)
{
switch (session_type) {
case OGS_PDU_SESSION_TYPE_IPV4:
return OGS_PDP_EUA_IETF_IPV4;
case OGS_PDU_SESSION_TYPE_IPV6:
return OGS_PDP_EUA_IETF_IPV6;
case OGS_PDU_SESSION_TYPE_IPV4V6:
return OGS_PDP_EUA_IETF_IPV4V6;
default:
return OGS_ERROR;
}
}
int ogs_gtp1_eua_ietf_type_to_pdu_session_type(uint8_t eua_ietf_type)
{
switch (eua_ietf_type) {
case OGS_PDP_EUA_IETF_IPV4:
return OGS_PDU_SESSION_TYPE_IPV4;
case OGS_PDP_EUA_IETF_IPV6:
return OGS_PDU_SESSION_TYPE_IPV6;
case OGS_PDP_EUA_IETF_IPV4V6:
return OGS_PDU_SESSION_TYPE_IPV4V6;
default:
return OGS_ERROR;
}
}
int ogs_gtp1_eua_to_ip(const ogs_eua_t *eua, uint16_t eua_len, ogs_ip_t *ip,
uint8_t *pdu_session_type)
{

View File

@ -35,6 +35,9 @@ int ogs_gtp1_sockaddr_to_gsn_addr(const ogs_sockaddr_t *addr,
int ogs_gtp1_gsn_addr_to_ip(const ogs_gtp1_gsn_addr_t *gsnaddr, uint16_t gsnaddr_len,
ogs_ip_t *ip);
int ogs_gtp1_pdu_session_type_to_eua_ietf_type(uint8_t session_type);
int ogs_gtp1_eua_ietf_type_to_pdu_session_type(uint8_t eua_ietf_type);
int ogs_gtp1_eua_to_ip(const ogs_eua_t *eua, uint16_t eua_len, ogs_ip_t *ip,
uint8_t *pdu_session_type);

View File

@ -398,3 +398,318 @@ int16_t ogs_gtp1_build_qos_profile(ogs_tlv_octet_t *octet,
octet->len = 6;
return octet->len;
}
/* 7.7.28 MM Context */
/* TODO: UMTS support, see Figure 41 and Figure 42. */
int ogs_gtp1_build_mm_context(ogs_gtp1_tlv_mm_context_t *octet,
const ogs_gtp1_mm_context_decoded_t *decoded, uint8_t *data, int data_len)
{
uint8_t *ptr = data;
unsigned int i;
uint16_t *len_ptr;
uint16_t val16;
ogs_assert(octet);
ogs_assert(data);
ogs_assert((size_t)data_len >= 1);
octet->data = data;
#define CHECK_SPACE_ERR(bytes) \
if ((ptr - data) + (bytes) > data_len) \
return OGS_ERROR
CHECK_SPACE_ERR(1);
*ptr++ = (decoded->gupii & 0x01) << 7 |
(decoded->ugipai & 0x01) << 6 |
(decoded->used_gprs_protection_algo & 0x07) << 3 |
(decoded->ksi & 0x07);
CHECK_SPACE_ERR(1);
*ptr++ = (decoded->sec_mode & 0x03) << 6 |
(decoded->num_vectors & 0x07) << 3 |
(decoded->sec_mode == OGS_GTP1_SEC_MODE_USED_CIPHER_VALUE_UMTS_KEY_AND_QUINTUPLETS ?
decoded->used_cipher & 0x07 : 0x07);
CHECK_SPACE_ERR(sizeof(decoded->ck));
memcpy(ptr, &decoded->ck[0], sizeof(decoded->ck));
ptr += sizeof(decoded->ck);
CHECK_SPACE_ERR(sizeof(decoded->ik));
memcpy(ptr, &decoded->ik[0], sizeof(decoded->ik));
ptr += sizeof(decoded->ik);
/* Quintuplet Length */
CHECK_SPACE_ERR(2);
len_ptr = (uint16_t *)ptr; /* will be filled later */
ptr += 2;
for (i = 0; i < decoded->num_vectors; i++) {
CHECK_SPACE_ERR(sizeof(decoded->auth_quintuplets[0]));
memcpy(ptr, &decoded->auth_quintuplets[i].rand, sizeof(decoded->auth_quintuplets[i].rand));
ptr += sizeof(decoded->auth_quintuplets[i].rand);
*ptr++ = decoded->auth_quintuplets[i].xres_len;
memcpy(ptr, &decoded->auth_quintuplets[i].xres[0], decoded->auth_quintuplets[i].xres_len);
ptr += decoded->auth_quintuplets[i].xres_len;
memcpy(ptr, &decoded->auth_quintuplets[i].ck, sizeof(decoded->auth_quintuplets[i].ck));
ptr += sizeof(decoded->auth_quintuplets[i].ck);
memcpy(ptr, &decoded->auth_quintuplets[i].ik, sizeof(decoded->auth_quintuplets[i].ik));
ptr += sizeof(decoded->auth_quintuplets[i].ik);
*ptr++ = decoded->auth_quintuplets[i].autn_len;
memcpy(ptr, &decoded->auth_quintuplets[i].autn[0], decoded->auth_quintuplets[i].autn_len);
ptr += decoded->auth_quintuplets[i].autn_len;
}
*len_ptr = htobe16(ptr - (((uint8_t *)len_ptr) + 2));
CHECK_SPACE_ERR(sizeof(decoded->drx_param));
memcpy(ptr, &decoded->drx_param, sizeof(decoded->drx_param));
ptr += sizeof(decoded->drx_param);
if (decoded->ms_network_capability_len != 0) {
/* MS Network Capability Length */
CHECK_SPACE_ERR(1 + decoded->ms_network_capability_len);
*ptr++ = decoded->ms_network_capability_len;
memcpy(ptr, &decoded->ms_network_capability, decoded->ms_network_capability_len);
ptr += decoded->ms_network_capability_len;
} else {
CHECK_SPACE_ERR(1);
*ptr++ = 0;
}
if (decoded->imeisv_len != 0) {
/* Container Len */
CHECK_SPACE_ERR(2);
val16 = htobe16(2 + decoded->imeisv_len);
memcpy(ptr, &val16, 2);
ptr += 2;
/* Container (Mobile Identity IMEISV), TS 29.060 Table 47A */
CHECK_SPACE_ERR(2 + decoded->imeisv_len);
*ptr++ = 0x23;
*ptr++ = decoded->imeisv_len; /* Length of mobile identity contents */
memcpy(ptr, &decoded->imeisv[0], decoded->imeisv_len);
ptr += decoded->imeisv_len;
} else {
/* Container Len */
CHECK_SPACE_ERR(2);
*ptr++ = 0;
*ptr++ = 0;
}
if (decoded->nrsrna) {
CHECK_SPACE_ERR(2);
*ptr++ = 1;
*ptr++ = 0x01;
}
octet->len = (ptr - data);
return OGS_OK;
#undef CHECK_SPACE_ERR
}
/* The format of EUA in PDP Context is not exactly the same for the entire EUA,
* hence a separate function is required to encode the value part of the address,
* instead of using regular ogs_gtp1_ip_to_eua(). */
static int enc_pdp_ctx_as_eua(uint8_t pdu_session_type, const ogs_ip_t *ip,
uint8_t *data, int data_len)
{
switch (pdu_session_type)
{
case OGS_PDU_SESSION_TYPE_IPV4:
if (!ip->ipv4) {
ogs_error("EUA type IPv4 but no IPv4 address available");
return OGS_ERROR;
}
if (data_len < OGS_IPV4_LEN)
return OGS_ERROR;
memcpy(data, &ip->addr, OGS_IPV4_LEN);
return OGS_IPV4_LEN;
case OGS_PDU_SESSION_TYPE_IPV6:
if (!ip->ipv6) {
ogs_error("EUA type IPv4 but no IPv6 address available");
return OGS_ERROR;
}
if (data_len < OGS_IPV6_LEN)
return OGS_ERROR;
memcpy(data, ip->addr6, OGS_IPV6_LEN);
return OGS_IPV6_LEN;
case OGS_PDU_SESSION_TYPE_IPV4V6:
if (ip->ipv4 && ip->ipv6) {
if (data_len < OGS_IPV4_LEN + OGS_IPV6_LEN)
return OGS_ERROR;
memcpy(data, &ip->addr, OGS_IPV4_LEN);
memcpy(data + OGS_IPV4_LEN, ip->addr6, OGS_IPV6_LEN);
return OGS_IPV4_LEN + OGS_IPV6_LEN;
} else if (ip->ipv4) {
if (data_len < OGS_IPV4_LEN)
return OGS_ERROR;
memcpy(data, &ip->addr, OGS_IPV4_LEN);
return OGS_IPV4_LEN;
} else if (ip->ipv6) {
if (data_len < OGS_IPV6_LEN)
return OGS_ERROR;
memcpy(data, ip->addr6, OGS_IPV6_LEN);
return OGS_IPV6_LEN;
} else {
ogs_error("EUA type IPv4 but no IPv4 nor IPv6 address available");
return OGS_ERROR;
}
break;
default:
ogs_error("Unexpected session type");
return OGS_ERROR;
}
return OGS_OK;
}
/* TS 29.060 7.7.29 PDP Context */
int ogs_gtp1_build_pdp_context(ogs_tlv_octet_t *octet,
const ogs_gtp1_pdp_context_decoded_t *decoded, uint8_t *data, int data_len)
{
uint8_t *ptr = data;
uint16_t val16;
uint32_t val32;
int rv;
ogs_tlv_octet_t qos_sub_tlv_unused;
ogs_assert(octet);
ogs_assert(data);
ogs_assert((size_t)data_len >= 1);
octet->data = data;
#define CHECK_SPACE_ERR(bytes) \
if ((ptr - data) + (bytes) > data_len) \
return OGS_ERROR
CHECK_SPACE_ERR(1);
*ptr++ = (decoded->ea << 7) | (decoded->vaa << 6) |
(decoded->asi << 5)| (decoded->order << 4) |
(decoded->nsapi & 0x0f);
CHECK_SPACE_ERR(1);
*ptr++ = (decoded->sapi & 0x0f);
/* Quality of Service Subscribed */
CHECK_SPACE_ERR(1 + OGS_GTP1_QOS_PROFILE_MAX_LEN);
rv = ogs_gtp1_build_qos_profile(&qos_sub_tlv_unused, &decoded->qos_sub,
ptr, (data + data_len) - (ptr + 1));
if (rv < 0)
return rv;
*ptr = rv;
ptr += 1 + rv;
/* Quality of Service Requested */
CHECK_SPACE_ERR(1 + OGS_GTP1_QOS_PROFILE_MAX_LEN);
rv = ogs_gtp1_build_qos_profile(&qos_sub_tlv_unused, &decoded->qos_req,
ptr, (data + data_len) - (ptr + 1));
if (rv < 0)
return rv;
*ptr = rv;
ptr += 1 + rv;
/* Quality of Service Negotiated */
CHECK_SPACE_ERR(1 + OGS_GTP1_QOS_PROFILE_MAX_LEN);
rv = ogs_gtp1_build_qos_profile(&qos_sub_tlv_unused, &decoded->qos_neg,
ptr, (data + data_len) - (ptr + 1));
if (rv < 0)
return rv;
*ptr = rv;
ptr += 1 + rv;
CHECK_SPACE_ERR(2);
val16 = htobe16(decoded->snd);
memcpy(ptr, &val16, 2);
ptr += 2;
CHECK_SPACE_ERR(2);
val16 = htobe16(decoded->snu);
memcpy(ptr, &val16, 2);
ptr += 2;
CHECK_SPACE_ERR(1);
*ptr++ = decoded->send_npdu_nr;
CHECK_SPACE_ERR(1);
*ptr++ = decoded->receive_npdu_nr;
CHECK_SPACE_ERR(4);
val32 = htobe32(decoded->ul_teic);
memcpy(ptr, &val32, 4);
ptr += 4;
CHECK_SPACE_ERR(4);
val32 = htobe32(decoded->ul_teid);
memcpy(ptr, &val32, 4);
ptr += 4;
CHECK_SPACE_ERR(1);
*ptr++ = decoded->pdp_ctx_id;
CHECK_SPACE_ERR(1);
*ptr++ = 0xf0 | decoded->pdp_type_org;
CHECK_SPACE_ERR(1);
*ptr++ = ogs_gtp1_pdu_session_type_to_eua_ietf_type(decoded->pdp_type_num[0]);
/* PDP Address Length filled after PDP Address */
CHECK_SPACE_ERR(1);
rv = enc_pdp_ctx_as_eua(decoded->pdp_type_num[0], &decoded->pdp_address[0],
ptr + 1, (data + data_len) - (ptr + 1));
if (rv < 0)
return rv;
*ptr = rv;
ptr += 1 + rv;
/* GGSN Address for control plane Length */
CHECK_SPACE_ERR(1);
*ptr = decoded->ggsn_address_c.ipv6 ? OGS_GTP_GSN_ADDRESS_IPV6_LEN : OGS_GTP_GSN_ADDRESS_IPV4_LEN;
CHECK_SPACE_ERR(1 + (*ptr));
memcpy(ptr + 1,
decoded->ggsn_address_c.ipv6 ?
(uint8_t *)decoded->ggsn_address_c.addr6 :
(uint8_t *)&decoded->ggsn_address_c.addr,
*ptr);
ptr += 1 + *ptr;
/* GGSN Address for User Traffic Length */
CHECK_SPACE_ERR(1);
*ptr = decoded->ggsn_address_u.ipv6 ? OGS_GTP_GSN_ADDRESS_IPV6_LEN : OGS_GTP_GSN_ADDRESS_IPV4_LEN;
CHECK_SPACE_ERR(1 + (*ptr));
memcpy(ptr + 1,
decoded->ggsn_address_u.ipv6 ?
(uint8_t *)decoded->ggsn_address_u.addr6 :
(uint8_t *)&decoded->ggsn_address_u.addr,
*ptr);
ptr += 1 + *ptr;
/* APN */
rv = strlen(decoded->apn);
CHECK_SPACE_ERR(1 + rv + 1);
*ptr = ogs_fqdn_build(
(char *)ptr + 1, decoded->apn, rv);
ptr += 1 + *ptr;
CHECK_SPACE_ERR(2);
*ptr++ = (decoded->trans_id >> 8) & 0x0f;
*ptr++ = decoded->trans_id & 0xff;
if (decoded->ea == OGS_GTP1_PDPCTX_EXT_EUA_YES) {
CHECK_SPACE_ERR(1);
*ptr++ = decoded->pdp_type_num[1];
/* PDP Address Length filled after PDP Address */
CHECK_SPACE_ERR(1);
rv = enc_pdp_ctx_as_eua(decoded->pdp_type_num[1], &decoded->pdp_address[1],
ptr + 1, (data + data_len) - (ptr + 1));
if (rv < 0)
return rv;
*ptr = rv;
ptr += 1 + rv;
}
octet->len = (ptr - data);
return OGS_OK;
#undef CHECK_SPACE_ERR
}

View File

@ -211,6 +211,18 @@ typedef struct ogs_gtp1_gsn_addr_s {
#define OGS_GTP1_APN_RESTRICTION_PRIVATE_1 3
#define OGS_GTP1_APN_RESTRICTION_PRIVATE_2 4
/* Table 10.5.156/3GPP TS 24.008 Quality of service */
/* Delivery order, octet 6 (see also 3GPP TS 23.107) */
#define OGS_GTP1_DELIVERY_ORDER_SUBSCRIBED 0
#define OGS_GTP1_DELIVERY_ORDER_YES 1
#define OGS_GTP1_DELIVERY_ORDER_NO 2
/* Delivery of erroneous SDUs, octet 6 (see also 3GPP TS 23.107) */
#define OGS_GTP1_DELIVERY_ERR_SDU_SUBSCRIBED 0
#define OGS_GTP1_DELIVERY_ERR_SDU_NO_DETECT 1
#define OGS_GTP1_DELIVERY_ERR_SDU_YES 2
#define OGS_GTP1_DELIVERY_ERR_SDU_NO 3
/* TS 29.060 7.7.34 Quality of Service (QoS) Profile */
#define OGS_GTP1_QOS_PROFILE_MAX_LEN 255
/* TS 24.008 10.5.6.5 Quality of service */
@ -300,4 +312,112 @@ int16_t ogs_gtp1_parse_qos_profile(
int16_t ogs_gtp1_build_qos_profile(ogs_tlv_octet_t *octet,
const ogs_gtp1_qos_profile_decoded_t *decoded, void *data, int data_len);
/* 7.7.7 Authentication Triplet. FIXME: Not used in MME Gn scenario? */
# if 0
struct ogs_gtp1_auth_triplet_s {
uint8_t rand[16];
uint8_t sres[4];
uint8_t kc[8];
} __attribute__ ((packed)) ogs_gtp1_auth_triplet_t;
#endif
/* 7.7.35 Authentication Quintuplet */
typedef struct ogs_gtp1_auth_quintuplet_s {
uint8_t rand[OGS_RAND_LEN];
uint8_t xres_len;
uint8_t xres[OGS_MAX_RES_LEN];
uint8_t ck[128/8];
uint8_t ik[128/8];
uint8_t autn_len;
uint8_t autn[OGS_AUTN_LEN];
} ogs_gtp1_auth_quintuplet_t;
/* TS 24.008 10.5.5.6 DRX parameter (value part only) */
typedef struct ogs_gtp1_drx_param_val_s {
uint8_t split_pg_cycle_code; /* 0 = equivalent to no DRX */
ED3(uint8_t cn_drx_cycle_len_coeff:4;,
uint8_t split_on_ccch:1;,
uint8_t non_drx_timer:3;)
} __attribute__ ((packed)) ogs_gtp1_drx_param_val_t;
/* 7.7.28 MM Context (Figure 41) */
/* Table 47: Security Mode Values */
#define OGS_GTP1_SEC_MODE_GSM_KEY_AND_TRIPLETS 1
#define OGS_GTP1_SEC_MODE_GSM_KEY_AND_QUINTUPLETS 3
#define OGS_GTP1_SEC_MODE_UMTS_KEY_AND_QUINTUPLETS 2
#define OGS_GTP1_SEC_MODE_USED_CIPHER_VALUE_UMTS_KEY_AND_QUINTUPLETS 0
/* Table 47B: Used GPRS integrity protection algorithm Values */
#define OGS_GTP1_USED_GPRS_IP_NO_IP 0
#define OGS_GTP1_USED_GPRS_IP_GIA4 4
#define OGS_GTP1_USED_GPRS_IP_GIA5 5
typedef struct ogs_gtp1_mm_context_decoded_s {
uint8_t gupii:1;
uint8_t ugipai:1;
uint8_t used_gprs_protection_algo:3; /* OGS_GTP1_USED_GPRS_IP */
uint8_t ksi:3;
uint8_t sec_mode:2; /* OGS_GTP1_SEC_MODE_* */
uint8_t num_vectors:3;
uint8_t used_cipher:3; /* 0..7 -> GEA/... */
uint8_t ck[OGS_SHA256_DIGEST_SIZE/2];
uint8_t ik[OGS_SHA256_DIGEST_SIZE/2];
ogs_gtp1_auth_quintuplet_t auth_quintuplets[5];
ogs_gtp1_drx_param_val_t drx_param;
uint8_t ms_network_capability_len;
uint8_t ms_network_capability[6]; /* ogs_nas_ms_network_capability_t */
uint8_t imeisv_len;
uint8_t imeisv[10]; /* ogs_nas_mobile_identity_imeisv_t */
uint8_t nrsrna;
} ogs_gtp1_mm_context_decoded_t;
int ogs_gtp1_build_mm_context(ogs_gtp1_tlv_mm_context_t *octet,
const ogs_gtp1_mm_context_decoded_t *decoded, uint8_t *data, int data_len);
/* Extended End User Address. Not explicitly defined in a table: */
#define OGS_GTP1_PDPCTX_EXT_EUA_NO 0
#define OGS_GTP1_PDPCTX_EXT_EUA_YES 1
/* 7.7.29 Table 48 Reordering Required Values */
#define OGS_GTP1_PDPCTX_REORDERING_REQUIRED_NO 0
#define OGS_GTP1_PDPCTX_REORDERING_REQUIRED_YES 1
/* 7.7.29 Table 49 VPLMN Address Allowed Values */
#define OGS_GTP1_PDPCTX_VLPMN_ADDR_ALLOWED_NO 0
#define OGS_GTP1_PDPCTX_VLPMN_ADDR_ALLOWED_YES 1
/* 7.7.29 Table 49A Activity Status Indicator Value */
#define OGS_GTP1_PDPCTX_ACTIVITY_STATUS_IND_YES 0
#define OGS_GTP1_PDPCTX_ACTIVITY_STATUS_IND_NO 1
/* 7.7.29 PDP Context */
typedef struct ogs_gtp1_pdp_context_decoded_s {
uint8_t ea:1; /* OGS_GTP1_PDPCTX_EXT_EUA_* */
uint8_t vaa:1; /* OGS_GTP1_PDPCTX_VLPMN_ADDR_ALLOWED_* */
uint8_t asi:1; /* OGS_GTP1_PDPCTX_ACTIVITY_STATUS_IND_* */
uint8_t order:1; /* OGS_GTP1_PDPCTX_REORDERING_REQUIRED_* */
uint8_t nsapi:4;
uint8_t sapi:4;
ogs_gtp1_qos_profile_decoded_t qos_sub;
ogs_gtp1_qos_profile_decoded_t qos_req;
ogs_gtp1_qos_profile_decoded_t qos_neg;
uint16_t snd;
uint16_t snu;
uint8_t send_npdu_nr;
uint8_t receive_npdu_nr;
uint32_t ul_teic;
uint32_t ul_teid;
uint8_t pdp_ctx_id;
uint8_t pdp_type_org;
uint8_t pdp_type_num[2];
ogs_ip_t pdp_address[2];
ogs_ip_t ggsn_address_c;
ogs_ip_t ggsn_address_u;
char apn[OGS_MAX_APN_LEN+1];
uint16_t trans_id:12;
} ogs_gtp1_pdp_context_decoded_t;
int ogs_gtp1_build_pdp_context(ogs_gtp1_tlv_pdp_context_t *octet,
const ogs_gtp1_pdp_context_decoded_t *decoded, uint8_t *data, int data_len);
#endif /* OGS_GTP1_TYPES_H */

View File

@ -47,6 +47,7 @@ static OGS_POOL(mme_csmap_pool, mme_csmap_t);
static OGS_POOL(mme_enb_pool, mme_enb_t);
static OGS_POOL(mme_ue_pool, mme_ue_t);
static OGS_POOL(mme_s11_teid_pool, ogs_pool_id_t);
static OGS_POOL(mme_gn_teid_pool, ogs_pool_id_t);
static OGS_POOL(enb_ue_pool, enb_ue_t);
static OGS_POOL(sgw_ue_pool, sgw_ue_t);
static OGS_POOL(mme_sess_pool, mme_sess_t);
@ -110,6 +111,8 @@ void mme_context_init(void)
ogs_pool_init(&mme_ue_pool, ogs_app()->max.ue);
ogs_pool_init(&mme_s11_teid_pool, ogs_app()->max.ue);
ogs_pool_random_id_generate(&mme_s11_teid_pool);
ogs_pool_init(&mme_gn_teid_pool, ogs_global_conf()->max.ue);
ogs_pool_random_id_generate(&mme_gn_teid_pool);
ogs_pool_init(&enb_ue_pool, ogs_app()->max.ue);
ogs_pool_init(&sgw_ue_pool, ogs_app()->max.ue);
@ -128,6 +131,8 @@ void mme_context_init(void)
ogs_assert(self.guti_ue_hash);
self.mme_s11_teid_hash = ogs_hash_make();
ogs_assert(self.mme_s11_teid_hash);
self.mme_gn_teid_hash = ogs_hash_make();
ogs_assert(self.mme_gn_teid_hash);
ogs_list_init(&self.mme_ue_list);
@ -158,12 +163,15 @@ void mme_context_final(void)
ogs_hash_destroy(self.guti_ue_hash);
ogs_assert(self.mme_s11_teid_hash);
ogs_hash_destroy(self.mme_s11_teid_hash);
ogs_assert(self.mme_gn_teid_hash);
ogs_hash_destroy(self.mme_gn_teid_hash);
ogs_pool_final(&m_tmsi_pool);
ogs_pool_final(&mme_bearer_pool);
ogs_pool_final(&mme_sess_pool);
ogs_pool_final(&mme_ue_pool);
ogs_pool_final(&mme_s11_teid_pool);
ogs_pool_final(&mme_gn_teid_pool);
ogs_pool_final(&enb_ue_pool);
ogs_pool_final(&sgw_ue_pool);
@ -3047,19 +3055,32 @@ mme_ue_t *mme_ue_add(enb_ue_t *enb_ue)
}
mme_ue->t_implicit_detach.pkbuf = NULL;
mme_ue->gn.t_gn_holding = ogs_timer_add(
ogs_app()->timer_mgr, mme_timer_gn_holding_timer_expire, mme_ue);
if (! mme_ue->gn.t_gn_holding) {
ogs_error("ogs_timer_add() failed");
ogs_pool_free(&mme_ue_pool, mme_ue);
return NULL;
}
mme_ebi_pool_init(mme_ue);
ogs_list_init(&mme_ue->sess_list);
/* Set MME-S11_TEID */
/* Set MME-S11-TEID */
ogs_pool_alloc(&mme_s11_teid_pool, &mme_ue->mme_s11_teid_node);
ogs_assert(mme_ue->mme_s11_teid_node);
mme_ue->mme_s11_teid = *(mme_ue->mme_s11_teid_node);
ogs_hash_set(self.mme_s11_teid_hash,
&mme_ue->mme_s11_teid, sizeof(mme_ue->mme_s11_teid), mme_ue);
/* Set MME-Gn-TEID */
ogs_pool_alloc(&mme_gn_teid_pool, &mme_ue->gn.mme_gn_teid_node);
ogs_assert(mme_ue->gn.mme_gn_teid_node);
mme_ue->gn.mme_gn_teid = *(mme_ue->gn.mme_gn_teid_node);
ogs_hash_set(self.mme_gn_teid_hash,
&mme_ue->gn.mme_gn_teid, sizeof(mme_ue->gn.mme_gn_teid), mme_ue);
/*
* When used for the first time, if last node is set,
* the search is performed from the first SGW in a round-robin manner.
@ -3103,6 +3124,8 @@ void mme_ue_remove(mme_ue_t *mme_ue)
ogs_hash_set(self.mme_s11_teid_hash,
&mme_ue->mme_s11_teid, sizeof(mme_ue->mme_s11_teid), NULL);
ogs_hash_set(self.mme_gn_teid_hash,
&mme_ue->gn.mme_gn_teid, sizeof(mme_ue->gn.mme_gn_teid), NULL);
ogs_assert(mme_ue->sgw_ue);
sgw_ue_remove(mme_ue->sgw_ue);
@ -3141,6 +3164,7 @@ void mme_ue_remove(mme_ue_t *mme_ue)
ogs_timer_delete(mme_ue->t3470.timer);
ogs_timer_delete(mme_ue->t_mobile_reachable.timer);
ogs_timer_delete(mme_ue->t_implicit_detach.timer);
ogs_timer_delete(mme_ue->gn.t_gn_holding);
enb_ue_unlink(mme_ue);
@ -3150,6 +3174,7 @@ void mme_ue_remove(mme_ue_t *mme_ue)
mme_ebi_pool_final(mme_ue);
ogs_pool_free(&mme_s11_teid_pool, mme_ue->mme_s11_teid_node);
ogs_pool_free(&mme_gn_teid_pool, mme_ue->gn.mme_gn_teid_node);
ogs_pool_free(&mme_ue_pool, mme_ue);
ogs_info("[Removed] Number of MME-UEs is now %d",
@ -3223,11 +3248,16 @@ mme_ue_t *mme_ue_find_by_guti(ogs_nas_eps_guti_t *guti)
self.guti_ue_hash, guti, sizeof(ogs_nas_eps_guti_t));
}
mme_ue_t *mme_ue_find_by_teid(uint32_t teid)
mme_ue_t *mme_ue_find_by_s11_local_teid(uint32_t teid)
{
return ogs_hash_get(self.mme_s11_teid_hash, &teid, sizeof(teid));
}
mme_ue_t *mme_ue_find_by_gn_local_teid(uint32_t teid)
{
return ogs_hash_get(self.mme_gn_teid_hash, &teid, sizeof(teid));
}
mme_ue_t *mme_ue_find_by_message(ogs_nas_eps_message_t *message)
{
mme_ue_t *mme_ue = NULL;

View File

@ -194,6 +194,7 @@ typedef struct mme_context_s {
ogs_hash_t *guti_ue_hash; /* hash table (GUTI : MME_UE) */
ogs_hash_t *mme_s11_teid_hash; /* hash table (MME-S11-TEID : MME_UE) */
ogs_hash_t *mme_gn_teid_hash; /* hash table (MME-GN-TEID : MME_UE) */
struct {
struct {
@ -423,6 +424,15 @@ struct mme_ue_s {
char a_msisdn_bcd[OGS_MAX_MSISDN_BCD_LEN+1];
mme_p_tmsi_t p_tmsi;
struct {
ogs_pool_id_t *mme_gn_teid_node; /* A node of MME-Gn-TEID */
uint32_t mme_gn_teid; /* MME-Gn-TEID is derived from NODE */
uint32_t sgsn_gn_teid;
ogs_ip_t sgsn_gn_ip;
ogs_ip_t sgsn_gn_ip_alt;
/* Unnamed timer in 3GPP TS 23.401 D.3.5 step 2), see also 3GPP TS 23.060 6.9.1.2.2 */
ogs_timer_t *t_gn_holding;
} gn;
struct {
mme_m_tmsi_t *m_tmsi;
@ -905,7 +915,8 @@ void mme_ue_fsm_fini(mme_ue_t *mme_ue);
mme_ue_t *mme_ue_find_by_imsi(uint8_t *imsi, int imsi_len);
mme_ue_t *mme_ue_find_by_imsi_bcd(char *imsi_bcd);
mme_ue_t *mme_ue_find_by_guti(ogs_nas_eps_guti_t *nas_guti);
mme_ue_t *mme_ue_find_by_teid(uint32_t teid);
mme_ue_t *mme_ue_find_by_s11_local_teid(uint32_t teid);
mme_ue_t *mme_ue_find_by_gn_local_teid(uint32_t teid);
mme_ue_t *mme_ue_find_by_message(ogs_nas_eps_message_t *message);
int mme_ue_set_imsi(mme_ue_t *mme_ue, char *imsi_bcd);

View File

@ -99,6 +99,8 @@ const char *mme_event_get_name(mme_event_t *e)
case MME_EVENT_GN_MESSAGE:
return "MME_EVENT_GN_MESSAGE";
case MME_EVENT_GN_TIMER:
return "MME_EVENT_GN_TIMER";
default:
break;
}

View File

@ -51,6 +51,7 @@ typedef enum {
MME_EVENT_SGSAP_LO_CONNREFUSED,
MME_EVENT_GN_MESSAGE,
MME_EVENT_GN_TIMER,
MAX_NUM_OF_MME_EVENT,

View File

@ -21,6 +21,276 @@
#include "mme-gn-build.h"
static int sess_fill_mm_context_decoded(mme_sess_t *sess, ogs_gtp1_mm_context_decoded_t *mmctx_dec)
{
mme_ue_t *mme_ue = sess->mme_ue;
mme_bearer_t *bearer = NULL;
*mmctx_dec = (ogs_gtp1_mm_context_decoded_t) {
.gupii = 1, /* Integrity Protection not required */
.ugipai = 1, /* Ignore "Used GPRS integrity protection algorithm" field" */
.ksi = mme_ue->nas_eps.ksi,
.sec_mode = OGS_GTP1_SEC_MODE_UMTS_KEY_AND_QUINTUPLETS,
.num_vectors = 0, /* TODO: figure out how to fill the quintuplets */
.drx_param = {
.split_pg_cycle_code = 0, /* equivalent to no DRX */
.cn_drx_cycle_len_coeff = 0,
.split_on_ccch = 0,
.non_drx_timer = 0,
},
.nrsrna = 0,
};
//TODO: derive cK Ki from mme_ue->kasme
ogs_kdf_ck_ik_idle_mobility(mme_ue->ul_count.i32, mme_ue->kasme, &mmctx_dec->ck[0], &mmctx_dec->ik[0]);
mmctx_dec->imeisv_len = sizeof(mme_ue->nas_mobile_identity_imeisv);
memcpy(&mmctx_dec->imeisv[0], &mme_ue->nas_mobile_identity_imeisv, sizeof(mme_ue->nas_mobile_identity_imeisv));
mmctx_dec->ms_network_capability_len = mme_ue->ms_network_capability.length;
memcpy(&mmctx_dec->ms_network_capability[0], ((uint8_t*)&mme_ue->ms_network_capability)+1, sizeof(mme_ue->ms_network_capability) - 1);
ogs_list_for_each(&sess->bearer_list, bearer) {
/* FIXME: only 1 PDP Context supported in the message so far. */
break;
}
return OGS_OK;
}
/* 3GPP TS 23.401 Annex E */
static void build_qos_profile_from_session(ogs_gtp1_qos_profile_decoded_t *qos_pdec,
const mme_sess_t *sess, const mme_bearer_t *bearer)
{
const mme_ue_t *mme_ue = sess->mme_ue;
const ogs_session_t *session = sess->session;
/* FIXME: Initialize with defaults: */
memset(qos_pdec, 0, sizeof(*qos_pdec));
qos_pdec->qos_profile.arp = session->qos.arp.priority_level;
/* 3GPP TS 23.107 "Delivery order should be set to 'no' for PDP Type =
* 'IPv4' or 'IPv6'. The SGSN shall ensure that the appropriate value is set."
* 3GPP TS 23.401 D.3.5 2b NOTE4: The GTP and PDCP sequence numbers are not
* relevant as the network does not configure usage of "delivery order
* required" [...] as described in clause "compatibility issues" (4.8.1) */
qos_pdec->qos_profile.data.delivery_order = OGS_GTP1_DELIVERY_ORDER_NO;
qos_pdec->qos_profile.data.delivery_erroneous_sdu = OGS_GTP1_DELIVERY_ERR_SDU_NO;
/* Maximum SDU Size: Encode it as 1500, the maximum for IP 3GPP TS 23.107 Table 4, Note 4) */
qos_pdec->qos_profile.data.max_sdu_size = 0x96;
/* 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 */
switch (session->qos.index) { /* QCI */
case 1:
qos_pdec->qos_profile.data.traffic_class = OGS_GTP1_QOS_TRAFFIC_CLASS_CONVERSATIONAL;
qos_pdec->qos_profile.data.source_statistics_descriptor = OGS_GTP1_QOS_SRC_STATS_DESC_SPEECH;
qos_pdec->dec_transfer_delay = 150;
qos_pdec->qos_profile.data.delay_class = 1;
break;
case 2:
qos_pdec->qos_profile.data.traffic_class = OGS_GTP1_QOS_TRAFFIC_CLASS_CONVERSATIONAL;
qos_pdec->qos_profile.data.source_statistics_descriptor = OGS_GTP1_QOS_SRC_STATS_DESC_UNKNOWN;
qos_pdec->dec_transfer_delay = 150;
qos_pdec->qos_profile.data.delay_class = 1;
break;
case 3:
qos_pdec->qos_profile.data.traffic_class = OGS_GTP1_QOS_TRAFFIC_CLASS_CONVERSATIONAL;
qos_pdec->qos_profile.data.source_statistics_descriptor = OGS_GTP1_QOS_SRC_STATS_DESC_UNKNOWN;
qos_pdec->dec_transfer_delay = 80;
qos_pdec->qos_profile.data.delay_class = 1;
break;
case 4:
qos_pdec->qos_profile.data.traffic_class = OGS_GTP1_QOS_TRAFFIC_CLASS_STREAMING;
qos_pdec->qos_profile.data.source_statistics_descriptor = OGS_GTP1_QOS_SRC_STATS_DESC_UNKNOWN;
qos_pdec->qos_profile.data.sdu_error_ratio = 5; /* 10^-5*/
qos_pdec->qos_profile.data.delay_class = 1;
break;
case 5:
qos_pdec->qos_profile.data.traffic_class = OGS_GTP1_QOS_TRAFFIC_CLASS_INTERACTIVE;
qos_pdec->qos_profile.data.traffic_handling_priority = 1;
qos_pdec->qos_profile.data.signalling_indication = 1;
qos_pdec->qos_profile.data.delay_class = qos_pdec->qos_profile.data.traffic_handling_priority;
break;
case 6:
qos_pdec->qos_profile.data.traffic_class = OGS_GTP1_QOS_TRAFFIC_CLASS_INTERACTIVE;
qos_pdec->qos_profile.data.traffic_handling_priority = 1;
qos_pdec->qos_profile.data.delay_class = qos_pdec->qos_profile.data.traffic_handling_priority;
break;
case 7:
qos_pdec->qos_profile.data.traffic_class = OGS_GTP1_QOS_TRAFFIC_CLASS_INTERACTIVE;
qos_pdec->qos_profile.data.traffic_handling_priority = 2;
qos_pdec->qos_profile.data.delay_class = qos_pdec->qos_profile.data.traffic_handling_priority;
break;
case 8:
qos_pdec->qos_profile.data.traffic_class = OGS_GTP1_QOS_TRAFFIC_CLASS_INTERACTIVE;
qos_pdec->qos_profile.data.traffic_handling_priority = 3;
qos_pdec->qos_profile.data.delay_class = qos_pdec->qos_profile.data.traffic_handling_priority;
break;
case 9:
default:
qos_pdec->qos_profile.data.traffic_class = OGS_GTP1_QOS_TRAFFIC_CLASS_BACKGROUND;
qos_pdec->qos_profile.data.delay_class = 4;
break;
}
qos_pdec->data_octet6_to_13_present = true;
qos_pdec->data_octet14_present = true;
qos_pdec->dec_mbr_kbps_dl = mme_ue->ambr.downlink / 1000;
qos_pdec->dec_mbr_kbps_ul = mme_ue->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;
}
static int sess_fill_pdp_context_decoded(mme_sess_t *sess, ogs_gtp1_pdp_context_decoded_t *pdpctx_dec)
{
mme_bearer_t *bearer = NULL;
*pdpctx_dec = (ogs_gtp1_pdp_context_decoded_t){
.ea = OGS_GTP1_PDPCTX_EXT_EUA_NO,
.vaa = OGS_GTP1_PDPCTX_VLPMN_ADDR_ALLOWED_YES,
.asi = OGS_GTP1_PDPCTX_ACTIVITY_STATUS_IND_NO,
.order = OGS_GTP1_PDPCTX_REORDERING_REQUIRED_NO,
/* 3GPP TS 23.401 Annex D3.5.5 2b.:
* "The GTP equence numbers received from the old 3G-SGSN are only relevant if
* delivery order is required for the PDP context (QoS profile)."
* NOTE 4: "The GTP and PDCP sequence numbers are not relevant" */
.snd = 0,
.snu = 0,
.send_npdu_nr = 0,
.receive_npdu_nr = 0,
.ul_teic = sess->pgw_s5c_teid,
.pdp_type_org = OGS_PDP_EUA_ORG_IETF,
.pdp_type_num = {sess->session->session_type, },
.pdp_address = {sess->session->ue_ip, },
.ggsn_address_c = sess->pgw_s5c_ip,
.trans_id = sess->pti,
};
ogs_cpystrn(pdpctx_dec->apn, sess->session->name, sizeof(pdpctx_dec->apn));
ogs_list_for_each(&sess->bearer_list, bearer) {
pdpctx_dec->nsapi = bearer->ebi;
pdpctx_dec->sapi = 3; /* FIXME. Using 3 = default for now. Maybe use 0 = UNASSIGNED ?*/
build_qos_profile_from_session(&pdpctx_dec->qos_sub, sess, bearer);
//FIXME: sort out where to get each one:
memcpy(&pdpctx_dec->qos_req, &pdpctx_dec->qos_sub, sizeof(pdpctx_dec->qos_sub));
memcpy(&pdpctx_dec->qos_neg, &pdpctx_dec->qos_sub, sizeof(pdpctx_dec->qos_sub));
pdpctx_dec->ul_teid = bearer->pgw_s5u_teid;
pdpctx_dec->pdp_ctx_id = 0; /* FIXME. */
pdpctx_dec->ggsn_address_u = bearer->pgw_s5u_ip;
/* FIXME: only 1 PDP Context supported in the message so far. */
break;
}
return OGS_OK;
}
/* 3GPP TS 29.060 7.5.4 SGSN Context Response */
ogs_pkbuf_t *mme_gn_build_sgsn_context_response(
mme_ue_t *mme_ue, uint8_t cause)
{
ogs_gtp1_message_t gtp1_message;
ogs_gtp1_sgsn_context_response_t *rsp = NULL;
mme_sess_t *sess = NULL;
ogs_gtp1_gsn_addr_t mme_gnc_gsnaddr;
int gsn_len;
int rv;
ogs_gtp1_mm_context_decoded_t mmctx_dec;
uint8_t mmctx_dec_buf[512];
ogs_gtp1_pdp_context_decoded_t pdpctx_dec;
uint8_t pdpctx_dec_buf[1024];
ogs_debug("[Gn] build SGSN Context Response");
rsp = &gtp1_message.sgsn_context_response;
memset(&gtp1_message, 0, sizeof(ogs_gtp1_message_t));
gtp1_message.h.type = OGS_GTP1_SGSN_CONTEXT_RESPONSE_TYPE;
/* 3GPP TS 29.060 7.7.1 Cause, Mandatory */
rsp->cause.presence = 1;
rsp->cause.u8 = cause;
/* 3GPP TS 29.060 7.7.2 IMSI, Conditional */
rsp->imsi.presence = !!mme_ue;
if (rsp->imsi.presence) {
rsp->imsi.data = mme_ue->imsi;
rsp->imsi.len = mme_ue->imsi_len;
}
if (cause != OGS_GTP1_CAUSE_REQUEST_ACCEPTED)
goto build_ret;
ogs_assert(mme_ue);
rsp->tunnel_endpoint_identifier_control_plane.presence = 1;
rsp->tunnel_endpoint_identifier_control_plane.u32 = mme_ue->gn.mme_gn_teid;
ogs_list_for_each(&mme_ue->sess_list, sess) {
if (!MME_HAVE_SGW_S1U_PATH(sess))
continue;
/* 7.7.28 MM Context */
rv = sess_fill_mm_context_decoded(sess, &mmctx_dec);
if (rv != OGS_OK) {
ogs_error("sess_fill_mm_context_decoded() failed");
return NULL;
}
rsp->mm_context.presence = 1;
rv = ogs_gtp1_build_mm_context(&rsp->mm_context, &mmctx_dec,
&mmctx_dec_buf[0], sizeof(mmctx_dec_buf));
if (rv != OGS_OK) {
ogs_error("ogs_gtp1_build_mm_context() failed");
return NULL;
}
/* 7.7.29 PDP Context */
rv = sess_fill_pdp_context_decoded(sess, &pdpctx_dec);
if (rv != OGS_OK) {
ogs_error("sess_fill_pdp_context_decoded() failed");
return NULL;
}
rsp->pdp_context.presence = 1;
rv = ogs_gtp1_build_pdp_context(&rsp->pdp_context, &pdpctx_dec,
&pdpctx_dec_buf[0], sizeof(pdpctx_dec_buf));
if (rv != OGS_OK) {
ogs_error("ogs_gtp1_build_pdp_context() failed");
return NULL;
}
/* FIXME: right now we only support encoding 1 context in the message. */
break;
}
/* SGSN Address for Control Plane */
if (ogs_gtp_self()->gtpc_addr6 &&
(mme_ue->gn.sgsn_gn_ip.ipv6 || mme_ue->gn.sgsn_gn_ip_alt.ipv6)) {
rv = ogs_gtp1_sockaddr_to_gsn_addr(NULL, ogs_gtp_self()->gtpc_addr6,
&mme_gnc_gsnaddr, &gsn_len);
if (rv != OGS_OK) {
ogs_error("ogs_gtp1_sockaddr_to_gsn_addr() failed");
return NULL;
}
} else {
rv = ogs_gtp1_sockaddr_to_gsn_addr(ogs_gtp_self()->gtpc_addr, NULL,
&mme_gnc_gsnaddr, &gsn_len);
if (rv != OGS_OK) {
ogs_error("ogs_gtp1_sockaddr_to_gsn_addr() failed");
return NULL;
}
}
rsp->sgsn_address_for_control_plane.presence = 1;
rsp->sgsn_address_for_control_plane.data = &mme_gnc_gsnaddr;
rsp->sgsn_address_for_control_plane.len = gsn_len;
build_ret:
return ogs_gtp1_build_msg(&gtp1_message);
}
/* 3GPP TS 29.060 7.5.14.1 RAN Information Relay */
ogs_pkbuf_t *mme_gn_build_ran_information_relay(
uint8_t type, const uint8_t *buf, size_t len,
@ -59,4 +329,4 @@ ogs_pkbuf_t *mme_gn_build_ran_information_relay(
gtp1_message.h.type = type;
return ogs_gtp1_build_msg(&gtp1_message);
}
}

View File

@ -30,6 +30,9 @@ extern "C" {
}
#endif
ogs_pkbuf_t *mme_gn_build_sgsn_context_response(
mme_ue_t *mme_ue, uint8_t cause);
ogs_pkbuf_t *mme_gn_build_ran_information_relay(
uint8_t type, const uint8_t *buf, size_t len,
const ogs_nas_rai_t *rai, uint16_t cell_id);

View File

@ -68,6 +68,164 @@ static int decode_global_enb_id(S1AP_Global_ENB_ID_t *glob_enb_id, const uint8_t
return OGS_OK;
}
/* 3GPP TS 23.003 2.8.2.2 Mapping from RAI and P-TMSI to GUT */
static void rai_ptmsi_to_guti(const ogs_nas_rai_t *rai, mme_p_tmsi_t ptmsi, uint32_t ptmsi_sig, ogs_nas_eps_guti_t *nas_guti)
{
uint16_t lac = be16toh(rai->lai.lac);;
nas_guti->nas_plmn_id =rai->lai.nas_plmn_id;
nas_guti->mme_gid = lac;
nas_guti->mme_code = rai->rac;
nas_guti->m_tmsi = 0xC0000000 | (ptmsi & 0x3f000000) | (ptmsi_sig & 0x00ff0000) | (ptmsi & 0x0000ffff);
}
/* TS 29.060 7.5.3 SGSN Context Request */
void mme_gn_handle_sgsn_context_request(
ogs_gtp_xact_t *xact, ogs_gtp1_sgsn_context_request_t *req)
{
ogs_nas_eps_guti_t nas_guti;
ogs_nas_rai_t *rai;
mme_ue_t *mme_ue = NULL;
int rv;
ogs_debug("[Gn] Rx SGSN Context Request");
if (!req->routeing_area_identity.presence) {
ogs_warn("[Gn] Rx SGSN Context Request with no RAI!");
mme_gtp1_send_sgsn_context_response(NULL, OGS_GTP1_CAUSE_MANDATORY_IE_MISSING, xact);
return;
}
if (req->routeing_area_identity.len != sizeof(*rai)) {
ogs_warn("[Gn] Rx SGSN Context Request RAI wrong size %u vs exp %zu!",
req->routeing_area_identity.len, sizeof(*rai));
mme_gtp1_send_sgsn_context_response(NULL, OGS_GTP1_CAUSE_MANDATORY_IE_INCORRECT, xact);
return;
}
if (!req->tunnel_endpoint_identifier_control_plane.presence) {
ogs_warn("[Gn] Rx SGSN Context Request with no Tunnel Endpoint Identifier Control Plane!");
mme_gtp1_send_sgsn_context_response(NULL, OGS_GTP1_CAUSE_MANDATORY_IE_MISSING, xact);
return;
}
if (!req->sgsn_address_for_control_plane.presence) {
ogs_warn("[Gn] Rx SGSN Context Request with no SGSN Address for Control Plane!");
mme_gtp1_send_sgsn_context_response(NULL, OGS_GTP1_CAUSE_MANDATORY_IE_MISSING, xact);
return;
}
if (!req->imsi.presence &&
!req->temporary_logical_link_identifier.presence &&
!req->packet_tmsi.presence) {
ogs_warn("[Gn] Rx SGSN Context Request with no IMSI/TLLI/P-TMSI!");
mme_gtp1_send_sgsn_context_response(NULL, OGS_GTP1_CAUSE_MANDATORY_IE_MISSING, xact);
return;
}
if (req->ms_validated.presence &&
(req->ms_validated.u8 & 0x01) /* 1=> "Yes" */
&& !req->imsi.presence) {
ogs_warn("[Gn] Rx SGSN Context Request with 'MS Validated' but no IMSI!");
mme_gtp1_send_sgsn_context_response(NULL, OGS_GTP1_CAUSE_MANDATORY_IE_MISSING, xact);
return;
}
rai = req->routeing_area_identity.data;
if (req->imsi.presence) {
mme_ue = mme_ue_find_by_imsi(req->imsi.data, req->imsi.len);
} else if (req->packet_tmsi.presence) { /* P-TMSI */
if (!req->p_tmsi_signature.presence) {
ogs_warn("[Gn] Rx SGSN Context Request with 'P-TMSI' but no P-TMSI Signature! Assuming value 0.");
req->p_tmsi_signature.u24 = 0;
}
rai_ptmsi_to_guti(rai, req->packet_tmsi.u32, req->p_tmsi_signature.u24, &nas_guti);
mme_ue = mme_ue_find_by_guti(&nas_guti);
} else if (req->temporary_logical_link_identifier.presence) {
if (!req->p_tmsi_signature.presence) {
ogs_warn("[Gn] Rx SGSN Context Request with 'TLLI' but no P-TMSI Signature! Assuming value 0.");
req->p_tmsi_signature.u24 = 0;
}
/* TS 29.060 7.5.3 "The TLLI/P-TMSI and RAI is a foreign TLLI/P-TMSI and an RAI in the old SGSN."
* A foregin TLLI is "tlli = (p_tmsi & 0x3fffffff) | 0x80000000", and since we only use 0x3fffffff
* bits of P-TMSI to derive the GUTI, it's totally fine passing the TLLI as P-TMSI. */
rai_ptmsi_to_guti(rai, req->temporary_logical_link_identifier.u32, req->p_tmsi_signature.u24, &nas_guti);
mme_ue = mme_ue_find_by_guti(&nas_guti);
}
if (!mme_ue) {
mme_gtp1_send_sgsn_context_response(NULL, OGS_GTP1_CAUSE_IMSI_IMEI_NOT_KNOWN, xact);
return;
}
mme_ue->gn.sgsn_gn_teid = req->tunnel_endpoint_identifier_control_plane.u32;
rv = ogs_gtp1_gsn_addr_to_ip(req->sgsn_address_for_control_plane.data,
req->sgsn_address_for_control_plane.len,
&mme_ue->gn.sgsn_gn_ip);
ogs_assert(rv == OGS_OK);
if (req->alternative_sgsn_address_for_control_plane.presence) {
rv = ogs_gtp1_gsn_addr_to_ip(req->alternative_sgsn_address_for_control_plane.data,
req->alternative_sgsn_address_for_control_plane.len,
&mme_ue->gn.sgsn_gn_ip_alt);
ogs_assert(rv == OGS_OK);
}
/* 3GPP TS 23.401 Annex D.3.5 "Routing Area Update":
* Step 2. "If the old P-TMSI Signature was valid or if the new SGSN indicates that it has authenticated the MS,
* the old SGSN starts a timer."
*/
ogs_timer_start(mme_ue->gn.t_gn_holding, mme_timer_cfg(MME_TIMER_GN_HOLDING)->duration);
mme_gtp1_send_sgsn_context_response(mme_ue, OGS_GTP1_CAUSE_REQUEST_ACCEPTED, xact);
}
/* TS 29.060 7.5.5 SGSN Context Acknowledge */
void mme_gn_handle_sgsn_context_acknowledge(
ogs_gtp_xact_t *xact, mme_ue_t *mme_ue, ogs_gtp1_sgsn_context_acknowledge_t *req)
{
int rv;
ogs_debug("[Gn] Rx SGSN Context Acknowledge");
rv = ogs_gtp_xact_commit(xact);
if (rv != OGS_OK) {
ogs_error("ogs_gtp_xact_commit() failed");
return;
}
if (!mme_ue) {
ogs_error("MME-UE Context has already been removed");
return;
}
/* 3GPP TS 23.060 6.9.1.2.2 Step 4), 3GPP TS 23.401 D.3.5 Step 4)
* The new SGSN sends an SGSN Context Acknowledge message to the old SGSN. The old MME (which is the old
* SGSN from the new SGSN's point of view) marks in its context that the information in the GWs and the HSS are
* invalid. This triggers the GWs, and the HSS to be updated if the UE initiates a Tracking Area Update procedure
* back to the old MME before completing the ongoing Routing Area Update procedure. If the security functions do
* not authenticate the MS correctly, then the routing area update shall be rejected, and the new SGSN shall send a
* reject indication to the old SGSN. The old MME shall continue as if the SGSN Context Request was never
* received.
* "NOTE 6: The new SGSN's operation is unmodified compared to pre-Rel-8. The old MME/S-GW (old SGSN from
* the new SGSN's point of view) does not forward any data towards the new SGSN." "*/
if (req->cause.u8 != OGS_GTP1_CAUSE_REQUEST_ACCEPTED) {
ogs_timer_stop(mme_ue->gn.t_gn_holding);
return;
}
/* 3GPP TS 23.060 6.9.1.2.2 Step 13)
* "If the old MME has an S1-MME association for the UE, the source MME sends a S1-U Release Command to the
* source eNodeB when receiving the SGSN Context Acknowledge message from the new SGSN. The RRC
* connection is released by the source eNodeB. The source eNodeB confirms the release of the RRC connection
* and of the S1-U connection by sending a S1-U Release Complete message to the source MME."
*/
if (mme_ue->enb_ue) {
rv = s1ap_send_ue_context_release_command(mme_ue->enb_ue,
S1AP_Cause_PR_nas, S1AP_CauseNas_normal_release,
S1AP_UE_CTX_REL_S1_REMOVE_AND_UNLINK, 0);
ogs_expect(rv == OGS_OK);
}
}
/* TS 29.060 7.5.14.1 */
void mme_gn_handle_ran_information_relay(
ogs_gtp_xact_t *xact, ogs_gtp1_ran_information_relay_t *req)

View File

@ -32,6 +32,12 @@ void mme_gn_handle_echo_request(
void mme_gn_handle_echo_response(
ogs_gtp_xact_t *xact, ogs_gtp1_echo_response_t *req);
void mme_gn_handle_sgsn_context_request(
ogs_gtp_xact_t *xact, ogs_gtp1_sgsn_context_request_t *req);
void mme_gn_handle_sgsn_context_acknowledge(
ogs_gtp_xact_t *xact, mme_ue_t *mme_ue, ogs_gtp1_sgsn_context_acknowledge_t *req);
void mme_gn_handle_ran_information_relay(
ogs_gtp_xact_t *xact, ogs_gtp1_ran_information_relay_t *req);
@ -39,4 +45,4 @@ void mme_gn_handle_ran_information_relay(
}
#endif
#endif /* MME_S11_HANDLER_H */
#endif /* MME_GN_HANDLER_H */

View File

@ -268,7 +268,7 @@ int mme_gtp_send_create_session_request(mme_sess_t *sess, int create_action)
return OGS_ERROR;
}
xact->create_action = create_action;
xact->local_teid = mme_ue->mme_s11_teid;
xact->local_teid = mme_ue->gn.mme_gn_teid;
rv = ogs_gtp_xact_commit(xact);
ogs_expect(rv == OGS_OK);
@ -307,7 +307,7 @@ int mme_gtp_send_modify_bearer_request(
return OGS_ERROR;
}
xact->modify_action = modify_action;
xact->local_teid = mme_ue->mme_s11_teid;
xact->local_teid = mme_ue->gn.mme_gn_teid;
rv = ogs_gtp_xact_commit(xact);
ogs_expect(rv == OGS_OK);
@ -346,7 +346,7 @@ int mme_gtp_send_delete_session_request(
return OGS_ERROR;
}
xact->delete_action = action;
xact->local_teid = mme_ue->mme_s11_teid;
xact->local_teid = mme_ue->gn.mme_gn_teid;
rv = ogs_gtp_xact_commit(xact);
ogs_expect(rv == OGS_OK);
@ -537,7 +537,7 @@ int mme_gtp_send_release_access_bearers_request(mme_ue_t *mme_ue, int action)
return OGS_ERROR;
}
xact->release_action = action;
xact->local_teid = mme_ue->mme_s11_teid;
xact->local_teid = mme_ue->gn.mme_gn_teid;
rv = ogs_gtp_xact_commit(xact);
ogs_expect(rv == OGS_OK);
@ -670,7 +670,7 @@ int mme_gtp_send_create_indirect_data_forwarding_tunnel_request(
ogs_error("ogs_gtp_xact_local_create() failed");
return OGS_ERROR;
}
xact->local_teid = mme_ue->mme_s11_teid;
xact->local_teid = mme_ue->gn.mme_gn_teid;
rv = ogs_gtp_xact_commit(xact);
ogs_expect(rv == OGS_OK);
@ -709,7 +709,7 @@ int mme_gtp_send_delete_indirect_data_forwarding_tunnel_request(
return OGS_ERROR;
}
xact->delete_indirect_action = action;
xact->local_teid = mme_ue->mme_s11_teid;
xact->local_teid = mme_ue->gn.mme_gn_teid;
rv = ogs_gtp_xact_commit(xact);
ogs_expect(rv == OGS_OK);
@ -750,7 +750,7 @@ int mme_gtp_send_bearer_resource_command(
return OGS_ERROR;
}
xact->xid |= OGS_GTP_CMD_XACT_ID;
xact->local_teid = mme_ue->mme_s11_teid;
xact->local_teid = mme_ue->gn.mme_gn_teid;
rv = ogs_gtp_xact_commit(xact);
ogs_expect(rv == OGS_OK);
@ -758,6 +758,35 @@ int mme_gtp_send_bearer_resource_command(
return rv;
}
int mme_gtp1_send_sgsn_context_response(
mme_ue_t *mme_ue, uint8_t cause, ogs_gtp_xact_t *xact)
{
int rv;
ogs_gtp1_header_t h;
ogs_pkbuf_t *pkbuf = NULL;
memset(&h, 0, sizeof(ogs_gtp1_header_t));
h.type = OGS_GTP1_SGSN_CONTEXT_RESPONSE_TYPE;
h.teid = mme_ue ? mme_ue->gn.sgsn_gn_teid : 0;
pkbuf = mme_gn_build_sgsn_context_response(mme_ue, cause);
if (!pkbuf) {
ogs_error("mme_gn_build_sgsn_context_response() failed");
return OGS_ERROR;
}
xact->local_teid = mme_ue ? mme_ue->gn.mme_gn_teid : 0;
rv = ogs_gtp1_xact_update_tx(xact, &h, pkbuf);
if (rv != OGS_OK) {
ogs_error("ogs_gtp1_xact_update_tx() failed");
return OGS_ERROR;
}
rv = ogs_gtp_xact_commit(xact);
ogs_expect(rv == OGS_OK);
return rv;
}
int mme_gtp1_send_ran_information_relay(
mme_sgsn_t *sgsn, const uint8_t *buf, size_t len,

View File

@ -55,6 +55,9 @@ int mme_gtp_send_delete_indirect_data_forwarding_tunnel_request(
int mme_gtp_send_bearer_resource_command(
mme_bearer_t *bearer, ogs_nas_eps_message_t *nas_message);
int mme_gtp1_send_sgsn_context_response(
mme_ue_t *mme_ue, uint8_t cause, ogs_gtp_xact_t *xact);
int mme_gtp1_send_ran_information_relay(
mme_sgsn_t *sgsn, const uint8_t *buf, size_t len,
const ogs_nas_rai_t *rai, uint16_t cell_id);

View File

@ -601,6 +601,8 @@ void mme_s11_handle_delete_session_response(
if (action == OGS_GTP_DELETE_IN_PATH_SWITCH_REQUEST) {
source_ue = sgw_ue_cycle(target_ue->source_ue);
if (!source_ue) /* InterRAT to 2G/3G (SGSN) case: */
source_ue = target_ue;
ogs_assert(source_ue);
} else {
source_ue = target_ue;

View File

@ -279,8 +279,19 @@ void mme_s6a_handle_clr(mme_ue_t *mme_ue, ogs_diam_s6a_message_t *s6a_message)
}
break;
case OGS_DIAM_S6A_CT_MME_UPDATE_PROCEDURE:
case OGS_DIAM_S6A_CT_SGSN_UPDATE_PROCEDURE:
mme_ue->detach_type = MME_DETACH_TYPE_HSS_IMPLICIT;
/* 3GPP TS 23.401 D.3.5.5 8), 3GPP TS 23.060 6.9.1.2.2 8):
* "When the timer described in step 2 is running, the MM and PDP/EPS
* Bearer Contexts and any affected S-GW resources are removed when the
* timer expires and the SGSN received a Cancel Location".
*/
if (mme_ue->gn.t_gn_holding->running) {
ogs_debug("Gn Holding Timer is running, delay removing UE resources");
break;
}
/*
* There is no need to send NAS or S1AP message to the UE.
* So, we don't have to check whether UE is IDLE or not.

View File

@ -524,14 +524,14 @@ void mme_state_operational(ogs_fsm_t *s, mme_event_t *e)
*/
if (gtp_message.h.teid_presence && gtp_message.h.teid != 0) {
/* Cause is not "Context not found" */
mme_ue = mme_ue_find_by_teid(gtp_message.h.teid);
mme_ue = mme_ue_find_by_s11_local_teid(gtp_message.h.teid);
} else if (xact->local_teid) { /* rx no TEID or TEID=0 */
/* 3GPP TS 29.274 5.5.2: we receive TEID=0 under some
* conditions, such as cause "Session context not found". In those
* cases, we still want to identify the local session which
* originated the message, so try harder by using the TEID we
* locally stored in xact when sending the original request: */
mme_ue = mme_ue_find_by_teid(xact->local_teid);
mme_ue = mme_ue_find_by_s11_local_teid(xact->local_teid);
}
switch (gtp_message.h.type) {
@ -651,6 +651,15 @@ void mme_state_operational(ogs_fsm_t *s, mme_event_t *e)
break;
}
if (gtp1_message.h.teid != 0) {
/* Cause is not "Context not found" */
mme_ue = mme_ue_find_by_gn_local_teid(gtp1_message.h.teid);
} else if (xact->local_teid) { /* rx no TEID or TEID=0 */
/* Try harder by using the TEID we locally stored in xact when
*sending the original request: */
mme_ue = mme_ue_find_by_gn_local_teid(xact->local_teid);
}
switch (gtp1_message.h.type) {
case OGS_GTP1_ECHO_REQUEST_TYPE:
mme_gn_handle_echo_request(xact, &gtp1_message.echo_request);
@ -658,6 +667,12 @@ void mme_state_operational(ogs_fsm_t *s, mme_event_t *e)
case OGS_GTP1_ECHO_RESPONSE_TYPE:
mme_gn_handle_echo_response(xact, &gtp1_message.echo_response);
break;
case OGS_GTP1_SGSN_CONTEXT_REQUEST_TYPE:
mme_gn_handle_sgsn_context_request(xact, &gtp1_message.sgsn_context_request);
break;
case OGS_GTP1_SGSN_CONTEXT_ACKNOWLEDGE_TYPE:
mme_gn_handle_sgsn_context_acknowledge(xact, mme_ue, &gtp1_message.sgsn_context_acknowledge);
break;
case OGS_GTP1_RAN_INFORMATION_RELAY_TYPE:
mme_gn_handle_ran_information_relay(xact, &gtp1_message.ran_information_relay);
break;
@ -668,6 +683,37 @@ void mme_state_operational(ogs_fsm_t *s, mme_event_t *e)
ogs_pkbuf_free(pkbuf);
break;
case MME_EVENT_GN_TIMER:
mme_ue = e->mme_ue;
ogs_assert(mme_ue);
sgw_ue = mme_ue->sgw_ue;
ogs_assert(sgw_ue);
switch (e->timer_id) {
case MME_TIMER_GN_HOLDING:
/* 3GPP TS 23.401 Annex D.3.5 "Routing Area Update":
* Step 13. "When the timer started in step 2) (see mme_gn_handle_sgsn_context_request()) expires the old MME
* releases any RAN and Serving GW resources. If the PLMN has configured Secondary RAT usage data reporting,
* the MME first releases RAN resource before releasing Serving GW resources."
*/
GTP_COUNTER_CLEAR(mme_ue,
GTP_COUNTER_DELETE_SESSION_BY_PATH_SWITCH);
ogs_list_for_each(&mme_ue->sess_list, sess) {
GTP_COUNTER_INCREMENT(
mme_ue, GTP_COUNTER_DELETE_SESSION_BY_PATH_SWITCH);
ogs_assert(OGS_OK ==
mme_gtp_send_delete_session_request(
sgw_ue, sess,
OGS_GTP_DELETE_IN_PATH_SWITCH_REQUEST));
}
break;
default:
ogs_error("Unknown timer[%s:%d]",
mme_timer_get_name(e->timer_id), e->timer_id);
}
break;
case MME_EVENT_SGSAP_LO_SCTP_COMM_UP:
sock = e->sock;
ogs_assert(sock);

View File

@ -58,6 +58,9 @@ static mme_timer_cfg_t g_mme_timer_cfg[MAX_NUM_OF_MME_TIMER] = {
[MME_TIMER_S11_HOLDING] =
{ .have = true, .duration = ogs_time_from_msec(300) },
[MME_TIMER_GN_HOLDING] =
{ .have = true, .duration = ogs_time_from_sec(20) },
};
static void emm_timer_event_send(
@ -258,3 +261,24 @@ void mme_timer_s11_holding_timer_expire(void *data)
mme_event_free(e);
}
}
void mme_timer_gn_holding_timer_expire(void *data)
{
int rv;
mme_event_t *e = NULL;
mme_ue_t *mme_ue;
ogs_assert(data);
mme_ue = data;
e = mme_event_new(MME_EVENT_GN_TIMER);
e->timer_id = MME_TIMER_GN_HOLDING;
e->mme_ue = mme_ue;
rv = ogs_queue_push(ogs_app()->queue, e);
if (rv != OGS_OK) {
ogs_error("ogs_queue_push() failed:%d", (int)rv);
mme_event_free(e);
}
}

View File

@ -45,6 +45,8 @@ typedef enum {
MME_TIMER_S11_HOLDING,
MME_TIMER_GN_HOLDING,
MME_TIMER_SGS_CLI_CONN_TO_SRV,
MAX_NUM_OF_MME_TIMER,
@ -76,6 +78,7 @@ void mme_timer_implicit_detach_expire(void *data);
void mme_timer_sgs_cli_conn_to_srv(void *data);
void mme_timer_s1_holding_timer_expire(void *data);
void mme_timer_s11_holding_timer_expire(void *data);
void mme_timer_gn_holding_timer_expire(void *data);
#ifdef __cplusplus
}

View File

@ -32,6 +32,23 @@ static void build_qos_profile_from_session(ogs_gtp1_qos_profile_decoded_t *qos_p
qos_pdec->qos_profile.arp = sess->session.qos.arp.priority_level;
/* 3GPP TS 23.107 "Delivery order should be set to 'no' for PDP Type =
* 'IPv4' or 'IPv6'. The SGSN shall ensure that the appropriate value is set."
* 3GPP TS 23.401 D.3.5 2b NOTE4: The GTP and PDCP sequence numbers are not
* relevant as the network does not configure usage of "delivery order
* required" [...] as described in clause "compatibility issues" (4.8.1) */
qos_pdec->qos_profile.data.delivery_order = OGS_GTP1_DELIVERY_ORDER_NO;
qos_pdec->qos_profile.data.delivery_erroneous_sdu = OGS_GTP1_DELIVERY_ERR_SDU_NO;
/* Maximum SDU Size: If value is set to a valid value, reuse it: */
if (sess->gtp.v1.qos_pdec.qos_profile.data.max_sdu_size >= 0x01 &&
sess->gtp.v1.qos_pdec.qos_profile.data.max_sdu_size <= 0x99) {
qos_pdec->qos_profile.data.max_sdu_size = sess->gtp.v1.qos_pdec.qos_profile.data.max_sdu_size;
} else { /* Encode it as 1500, the maximum for IP 3GPP TS 23.107 Table 4, Note 4) */
qos_pdec->qos_profile.data.max_sdu_size = 0x96;
}
/* 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 */
switch (sess->session.qos.index) { /* QCI */