forked from acouzens/open5gs
Compare commits
6 Commits
25a0169fa5
...
7579f351b6
Author | SHA1 | Date |
---|---|---|
Oliver Smith | 7579f351b6 | |
Pau Espin | e45eca76b6 | |
Pau Espin | e2b747546b | |
Pau Espin | 70210324f2 | |
Pau Espin | e84450bfc1 | |
Pau Espin | 4e08baa633 |
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ typedef enum {
|
|||
MME_EVENT_SGSAP_LO_CONNREFUSED,
|
||||
|
||||
MME_EVENT_GN_MESSAGE,
|
||||
MME_EVENT_GN_TIMER,
|
||||
|
||||
MAX_NUM_OF_MME_EVENT,
|
||||
|
||||
|
|
|
@ -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 = >p1_message.sgsn_context_response;
|
||||
memset(>p1_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(>p1_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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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, >p1_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, >p1_message.echo_response);
|
||||
break;
|
||||
case OGS_GTP1_SGSN_CONTEXT_REQUEST_TYPE:
|
||||
mme_gn_handle_sgsn_context_request(xact, >p1_message.sgsn_context_request);
|
||||
break;
|
||||
case OGS_GTP1_SGSN_CONTEXT_ACKNOWLEDGE_TYPE:
|
||||
mme_gn_handle_sgsn_context_acknowledge(xact, mme_ue, >p1_message.sgsn_context_acknowledge);
|
||||
break;
|
||||
case OGS_GTP1_RAN_INFORMATION_RELAY_TYPE:
|
||||
mme_gn_handle_ran_information_relay(xact, >p1_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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue