[#568] Add GTP-U Error Indication Handling

So far, no operation was performed when Error Indication was received
from eNodeB. For that reason, I solved #568 issues by controlling
the MME to prevent this from happening.

Now, when GTP-U Error Indication is received, MME and SGW are implemented
to do what they have to do. I hope that the network can be restored
by responding appropriately even if Error Indication occurs.
This commit is contained in:
Sukchan Lee 2020-10-20 20:00:02 -04:00
parent c244dcc5e9
commit 1bed0d5872
44 changed files with 1428 additions and 582 deletions

View File

@ -339,13 +339,16 @@ static void cluster_free(ogs_pkbuf_pool_t *pool, ogs_cluster_t *cluster)
ogs_pool_free(&pool->cluster_512, (ogs_cluster_512_t*)cluster->buffer);
break;
case OGS_CLUSTER_1024_SIZE:
ogs_pool_free(&pool->cluster_1024, (ogs_cluster_1024_t*)cluster->buffer);
ogs_pool_free(
&pool->cluster_1024, (ogs_cluster_1024_t*)cluster->buffer);
break;
case OGS_CLUSTER_2048_SIZE:
ogs_pool_free(&pool->cluster_2048, (ogs_cluster_2048_t*)cluster->buffer);
ogs_pool_free(
&pool->cluster_2048, (ogs_cluster_2048_t*)cluster->buffer);
break;
case OGS_CLUSTER_8192_SIZE:
ogs_pool_free(&pool->cluster_8192, (ogs_cluster_8192_t*)cluster->buffer);
ogs_pool_free(
&pool->cluster_8192, (ogs_cluster_8192_t*)cluster->buffer);
break;
case OGS_CLUSTER_BIG_SIZE:
ogs_pool_free(&pool->cluster_big, (ogs_cluster_big_t*)cluster->buffer);
@ -356,4 +359,3 @@ static void cluster_free(ogs_pkbuf_pool_t *pool, ogs_cluster_t *cluster)
ogs_pool_free(&pool->cluster, cluster);
}

View File

@ -110,6 +110,20 @@ static ogs_inline void ogs_pkbuf_put_u8(ogs_pkbuf_t *pkbuf, uint8_t val)
*(uint8_t *)ogs_pkbuf_put(pkbuf, 1) = val;
}
static ogs_inline void ogs_pkbuf_put_u16(ogs_pkbuf_t *pkbuf, uint16_t val)
{
uint8_t *p = ogs_pkbuf_put(pkbuf, 2);
uint16_t tmp = htobe16(val);
memcpy(p, &tmp, 2);
}
static ogs_inline void ogs_pkbuf_put_u32(ogs_pkbuf_t *pkbuf, uint32_t val)
{
uint8_t *p = ogs_pkbuf_put(pkbuf, 4);
uint32_t tmp = htobe32(val);
memcpy(p, &tmp, 4);
}
static ogs_inline void *ogs_pkbuf_push(ogs_pkbuf_t *pkbuf, unsigned int len)
{
pkbuf->data -= len;

View File

@ -300,7 +300,7 @@ const char *ogs_inet_ntop(void *sa, char *buf, int buflen)
ogs_assert(buflen >= OGS_ADDRSTRLEN);
family = sockaddr->ogs_sa_family;
switch(family) {
switch (family) {
case AF_INET:
return inet_ntop(family, &sockaddr->sin.sin_addr, buf,
INET_ADDRSTRLEN);

View File

@ -56,3 +56,56 @@ ogs_pkbuf_t *ogs_gtp_build_echo_response(
gtp_message.h.type = type;
return ogs_gtp_build_msg(&gtp_message);
}
ogs_pkbuf_t *ogs_gtp_build_error_indication(
uint32_t teid, ogs_sockaddr_t *addr)
{
ogs_pkbuf_t *pkbuf = NULL;
unsigned char *p = NULL;
int family;
ogs_assert(addr);
pkbuf = ogs_pkbuf_alloc(
NULL, 100 /* enough for Error Indiciation; use smaller buffer */);
ogs_assert(pkbuf);
ogs_pkbuf_reserve(pkbuf, OGS_GTPV1U_5GC_HEADER_LEN);
/*
* 8.3 Tunnel Endpoint Identifier Data I
*
* Octet 1 : Type = 16 (Decimal)
* Octet 2-5 : Tunnel Endpoint Identitifer Data I
*/
ogs_pkbuf_put_u8(pkbuf, 16);
ogs_pkbuf_put_u32(pkbuf, teid);
/*
* 8.4 GTP-U Peer Address
*
* Octet 1 : Type = 133 (Decimal)
* Octet 2-3 : Length
* Octet 4-n : IPv4 or IPv6 Address
*/
ogs_pkbuf_put_u8(pkbuf, 133);
family = addr->ogs_sa_family;
switch(family) {
case AF_INET:
ogs_pkbuf_put_u16(pkbuf, OGS_IPV4_LEN);
p = ogs_pkbuf_put(pkbuf, OGS_IPV4_LEN);
memcpy(p, &addr->sin.sin_addr, OGS_IPV4_LEN);
break;
case AF_INET6:
ogs_pkbuf_put_u16(pkbuf, OGS_IPV6_LEN);
p = ogs_pkbuf_put(pkbuf, OGS_IPV6_LEN);
memcpy(p, &addr->sin6.sin6_addr, OGS_IPV6_LEN);
break;
default:
ogs_fatal("Unknown family(%d)", family);
ogs_abort();
return NULL;
}
return pkbuf;
}

View File

@ -33,6 +33,9 @@ ogs_pkbuf_t *ogs_gtp_build_echo_request(
ogs_pkbuf_t *ogs_gtp_build_echo_response(
uint8_t type, uint8_t recovery, uint8_t features);
ogs_pkbuf_t *ogs_gtp_build_error_indication(
uint32_t teid, ogs_sockaddr_t *addr);
#ifdef __cplusplus
}
#endif

View File

@ -20,7 +20,7 @@
/*******************************************************************************
* This file had been created by gtp-tlv.py script v0.1.0
* Please do not modify this file but regenerate it via script.
* Created on: 2020-08-11 20:17:53.518172 by acetcom
* Created on: 2020-10-14 13:48:14.476575 by acetcom
* from 29274-g30.docx
******************************************************************************/

View File

@ -20,7 +20,7 @@
/*******************************************************************************
* This file had been created by gtp-tlv.py script v0.1.0
* Please do not modify this file but regenerate it via script.
* Created on: 2020-08-11 20:17:53.511069 by acetcom
* Created on: 2020-10-14 13:48:14.468022 by acetcom
* from 29274-g30.docx
******************************************************************************/
@ -50,9 +50,11 @@ typedef struct ogs_gtp_header_s {
uint8_t spare1:3;)
};
/* GTU-U flags */
#define OGS_GTPU_FLAGS_PN 0x1
#define OGS_GTPU_FLAGS_S 0x2
#define OGS_GTPU_FLAGS_E 0x4
#define OGS_GTPU_FLAGS_V 0x20
#define OGS_GTPU_FLAGS_PT 0x10
#define OGS_GTPU_FLAGS_E 0x04
#define OGS_GTPU_FLAGS_S 0x02
#define OGS_GTPU_FLAGS_PN 0x01
uint8_t flags;
};
/* GTP-U message type, defined in 3GPP TS 29.281 Release 11 */

View File

@ -123,6 +123,114 @@ int ogs_gtp_sendto(ogs_gtp_node_t *gnode, ogs_pkbuf_t *pkbuf)
return OGS_OK;
}
int ogs_gtp_send_user_plane(
ogs_gtp_node_t *gnode,
ogs_gtp_header_t *gtp_hdesc, ogs_gtp_extension_header_t *ext_hdesc,
ogs_pkbuf_t *pkbuf)
{
char buf[OGS_ADDRSTRLEN];
int rv;
ogs_gtp_header_t *gtp_h = NULL;
ogs_gtp_extension_header_t *ext_h = NULL;
uint8_t flags;
uint8_t gtp_hlen = 0;
ogs_assert(gnode);
ogs_assert(gtp_hdesc);
ogs_assert(ext_hdesc);
ogs_assert(pkbuf);
/* Processing GTP Flags */
flags = gtp_hdesc->flags;
flags |= OGS_GTPU_FLAGS_V | OGS_GTPU_FLAGS_PT;
if (ext_hdesc->qos_flow_identifier) flags |= OGS_GTPU_FLAGS_E;
/* Define GTP Header Size */
if (flags & OGS_GTPU_FLAGS_E)
gtp_hlen = OGS_GTPV1U_HEADER_LEN+8;
else if (flags & (OGS_GTPU_FLAGS_S|OGS_GTPU_FLAGS_PN))
gtp_hlen = OGS_GTPV1U_HEADER_LEN+4;
else
gtp_hlen = OGS_GTPV1U_HEADER_LEN;
ogs_pkbuf_push(pkbuf, gtp_hlen);
/* Fill GTP Header */
gtp_h = (ogs_gtp_header_t *)pkbuf->data;
ogs_assert(gtp_h);
memset(gtp_h, 0, gtp_hlen);
gtp_h->flags = flags;
gtp_h->type = gtp_hdesc->type;
if (gtp_h->type == OGS_GTPU_MSGTYPE_ECHO_REQ ||
gtp_h->type == OGS_GTPU_MSGTYPE_ECHO_RSP ||
gtp_h->type == OGS_GTPU_MSGTYPE_ERR_IND) {
/*
* TS29.281 5.1 General format in GTP-U header
*
* - The Echo Request/Response and Supported Extension Headers
* notification messages, where the Tunnel Endpoint Identifier
* shall be set to all zeroes.
* - The Error Indication message where the Tunnel Endpoint Identifier
* shall be set to all zeros.
*/
ogs_assert(gtp_hdesc->teid == 0);
}
gtp_h->teid = htobe32(gtp_hdesc->teid);
/*
* TS29.281 5.1 General format in GTP-U header
*
* Length: This field indicates the length in octets of the payload,
* i.e. the rest of the packet following the mandatory part of
* the GTP header (that is the first 8 octets). The Sequence Number,
* the N-PDU Number or any Extension headers shall be considered
* to be part of the payload, i.e. included in the length count.
*/
gtp_h->length = htobe16(pkbuf->len - OGS_GTPV1U_HEADER_LEN);
/* Fill Extention Header */
if (gtp_h->flags & OGS_GTPU_FLAGS_E) {
ext_h = (ogs_gtp_extension_header_t *)
(pkbuf->data + OGS_GTPV1U_HEADER_LEN);
ogs_assert(ext_h);
if (ext_hdesc->qos_flow_identifier) {
/* 5G Core */
ext_h->type = OGS_GTP_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER;
ext_h->len = 1;
ext_h->pdu_type =
OGS_GTP_EXTENSION_HEADER_PDU_TYPE_UL_PDU_SESSION_INFORMATION;
ext_h->qos_flow_identifier = ext_hdesc->qos_flow_identifier;
ext_h->next_type =
OGS_GTP_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS;
} else {
/* EPC */
ext_h->type = ext_hdesc->type;
ext_h->len = 1;
ext_h->next_type =
OGS_GTP_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS;
}
}
ogs_debug("SEND GTP-U[%d] to Peer[%s] : TEID[0x%x]",
gtp_hdesc->type, OGS_ADDR(&gnode->addr, buf), gtp_hdesc->teid);
rv = ogs_gtp_sendto(gnode, pkbuf);
if (rv != OGS_OK) {
if (ogs_socket_errno != OGS_EAGAIN) {
ogs_error("SEND GTP-U[%d] to Peer[%s] : TEID[0x%x]",
gtp_hdesc->type, OGS_ADDR(&gnode->addr, buf), gtp_hdesc->teid);
}
}
ogs_pkbuf_free(pkbuf);
return rv;
}
ogs_pkbuf_t *ogs_gtp_handle_echo_req(ogs_pkbuf_t *pkb)
{
ogs_gtp_header_t *gtph = NULL;

View File

@ -36,6 +36,11 @@ int ogs_gtp_connect(ogs_sock_t *ipv4, ogs_sock_t *ipv6, ogs_gtp_node_t *gnode);
int ogs_gtp_send(ogs_gtp_node_t *gnode, ogs_pkbuf_t *pkbuf);
int ogs_gtp_sendto(ogs_gtp_node_t *gnode, ogs_pkbuf_t *pkbuf);
int ogs_gtp_send_user_plane(
ogs_gtp_node_t *gnode,
ogs_gtp_header_t *gtp_hdesc, ogs_gtp_extension_header_t *ext_hdesc,
ogs_pkbuf_t *pkbuf);
ogs_pkbuf_t *ogs_gtp_handle_echo_req(ogs_pkbuf_t *pkt);
void ogs_gtp_send_error_message(
ogs_gtp_xact_t *xact, uint32_t teid, uint8_t type, uint8_t cause_value);

View File

@ -411,9 +411,11 @@ typedef struct ogs_gtp_header_s {
uint8_t spare1:3;)
};
/* GTU-U flags */
#define OGS_GTPU_FLAGS_PN 0x1
#define OGS_GTPU_FLAGS_S 0x2
#define OGS_GTPU_FLAGS_E 0x4
#define OGS_GTPU_FLAGS_V 0x20
#define OGS_GTPU_FLAGS_PT 0x10
#define OGS_GTPU_FLAGS_E 0x04
#define OGS_GTPU_FLAGS_S 0x02
#define OGS_GTPU_FLAGS_PN 0x01
uint8_t flags;
};
/* GTP-U message type, defined in 3GPP TS 29.281 Release 11 */

View File

@ -51,6 +51,7 @@ extern "C" {
#define OGS_GTPV1U_EXTENSION_HEADER_LEN 4
typedef struct ogs_gtp_extension_header_s {
#define OGS_GTP_EXTENSION_HEADER_TYPE_UDP_PORT 0x40
#define OGS_GTP_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER 0x85
#define OGS_GTP_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS 0x0
uint16_t sequence_number;
@ -68,12 +69,13 @@ typedef struct ogs_gtp_extension_header_s {
} __attribute__ ((packed)) ogs_gtp_extension_header_t;
/* 8.4 Cause */
#define OGS_GTP_CAUSE_LOCAL_DETACH 2
#define OGS_GTP_CAUSE_INVALID_VALUE 0
#define OGS_GTP_CAUSE_LOCAL_DETACH 2
#define OGS_GTP_CAUSE_COMPLETE_DETACH_3
#define OGS_GTP_CAUSE_RAT_CHANGED_FROM_3GPP_TO_NON_3GPP 4
#define OGS_GTP_CAUSE_ISR_DEACTIVATION 5
#define OGS_GTP_CAUSE_ERROR_INDICATION_RECEIVED_FROM_RNC_ENODEB_S4_SGSN_MME 6
#define OGS_GTP_CAUSE_IMSI_DETACH_ONLY 7
#define OGS_GTP_CAUSE_ISR_DEACTIVATION 5
#define OGS_GTP_CAUSE_ERROR_INDICATION_RECEIVED 6
#define OGS_GTP_CAUSE_IMSI_DETACH_ONLY 7
#define OGS_GTP_CAUSE_REACTIVATION_REQUESTED 8
#define OGS_GTP_CAUSE_PDN_RECONNECTION_TO_THIS_APN_DISALLOWED 9
#define OGS_GTP_CAUSE_ACCESS_CHANGED_FROM_NON_3GPP_TO_3GPP 10
@ -81,7 +83,7 @@ typedef struct ogs_gtp_extension_header_s {
#define OGS_GTP_CAUSE_PGW_NOT_RESPONDING 12
#define OGS_GTP_CAUSE_NETWORK_FAILURE 13
#define OGS_GTP_CAUSE_QOS_PARAMETER_MISMATCH 14
#define OGS_GTP_CAUSE_REQUEST_ACCEPTED 16
#define OGS_GTP_CAUSE_REQUEST_ACCEPTED 16
#define OGS_GTP_CAUSE_REQUEST_ACCEPTED_PARTIALLY 17
#define OGS_GTP_CAUSE_NEW_PDN_TYPE_DUE_TO_NETWORK_PREFERENCE 18
#define OGS_GTP_CAUSE_NEW_PDN_TYPE_DUE_TO_SINGLE_ADDRESS_BEARER_ONLY 19
@ -127,7 +129,7 @@ typedef struct ogs_gtp_extension_header_s {
#define OGS_GTP_CAUSE_DATA_FORWARDING_NOT_SUPPORTED 106
#define OGS_GTP_CAUSE_INVALID_REPLY_FROM_REMOTE_PEER 107
#define OGS_GTP_CAUSE_FALLBACK_TO_GTPV1 108
#define OGS_GTP_CAUSE_INVALID_PEER 109
#define OGS_GTP_CAUSE_INVALID_PEER 109
#define OGS_GTP_CAUSE_TEMPORARILY_REJECTED_DUE_TO_HANDOVER_IN_PROGRESS 110
#define OGS_GTP_CAUSE_MODIFICATIONS_NOT_LIMITED_TO_S1_U_BEARERS 111
#define OGS_GTP_CAUSE_REQUEST_REJECTED_FOR_A_PMIPV6_REASON 112

View File

@ -253,7 +253,6 @@ void ogs_pfcp_build_create_pdr(
ogs_pfcp_tlv_create_pdr_t *message, int i, ogs_pfcp_pdr_t *pdr)
{
ogs_pfcp_far_t *far = NULL;
ogs_pfcp_sess_t *pfcp_sess = NULL;
ogs_pfcp_sdf_filter_t pfcp_sdf_filter[OGS_MAX_NUM_OF_RULE];
int j = 0;
int len = 0;
@ -261,8 +260,6 @@ void ogs_pfcp_build_create_pdr(
ogs_assert(message);
ogs_assert(pdr);
pfcp_sess = pdr->sess;
ogs_assert(pfcp_sess);
far = pdr->far;
ogs_assert(far);
@ -412,12 +409,8 @@ static struct {
void ogs_pfcp_build_create_far(
ogs_pfcp_tlv_create_far_t *message, int i, ogs_pfcp_far_t *far)
{
ogs_pfcp_sess_t *pfcp_sess = NULL;
ogs_assert(message);
ogs_assert(far);
pfcp_sess = far->sess;
ogs_assert(pfcp_sess);
message->presence = 1;
message->far_id.presence = 1;
@ -637,6 +630,15 @@ ogs_pkbuf_t *ogs_pfcp_build_session_report_request(
}
}
if (report->error_indication.remote_f_teid_len) {
req->error_indication_report.presence = 1;
req->error_indication_report.remote_f_teid.presence = 1;
req->error_indication_report.remote_f_teid.data =
&report->error_indication.remote_f_teid;
req->error_indication_report.remote_f_teid.len =
report->error_indication.remote_f_teid_len;
}
pfcp_message.h.type = type;
return ogs_pfcp_build_msg(&pfcp_message);
}

View File

@ -92,6 +92,7 @@ void ogs_pfcp_context_init(int num_of_gtpu_resource)
ogs_pool_init(&ogs_pfcp_subnet_pool, OGS_MAX_NUM_OF_SUBNET);
self.pdr_hash = ogs_hash_make();
self.far_hash = ogs_hash_make();
context_initialized = 1;
}
@ -102,6 +103,8 @@ void ogs_pfcp_context_final(void)
ogs_assert(self.pdr_hash);
ogs_hash_destroy(self.pdr_hash);
ogs_assert(self.far_hash);
ogs_hash_destroy(self.far_hash);
ogs_pfcp_dev_remove_all();
ogs_pfcp_subnet_remove_all();
@ -857,32 +860,6 @@ ogs_pfcp_pdr_t *ogs_pfcp_pdr_find(
return NULL;
}
static uint64_t pdr_hash_keygen(uint32_t teid, uint8_t qfi)
{
uint64_t hashkey = (teid << 8) + qfi;
return hashkey;
}
void ogs_pfcp_pdr_hash_set(ogs_pfcp_pdr_t *pdr)
{
ogs_assert(pdr);
if (pdr->hashkey)
ogs_hash_set(ogs_pfcp_self()->pdr_hash, &pdr->hashkey,
sizeof(pdr->hashkey), NULL);
pdr->hashkey = pdr_hash_keygen(pdr->f_teid.teid, pdr->qfi);
ogs_hash_set(ogs_pfcp_self()->pdr_hash, &pdr->hashkey,
sizeof(pdr->hashkey), pdr);
}
ogs_pfcp_pdr_t *ogs_pfcp_pdr_find_by_teid_and_qfi(uint32_t teid, uint8_t qfi)
{
uint64_t hashkey = pdr_hash_keygen(teid, qfi);
return (ogs_pfcp_pdr_t *)ogs_hash_get(self.pdr_hash,
&hashkey, sizeof(hashkey));
}
ogs_pfcp_pdr_t *ogs_pfcp_pdr_find_or_add(
ogs_pfcp_sess_t *sess, ogs_pfcp_pdr_id_t id)
{
@ -900,6 +877,32 @@ ogs_pfcp_pdr_t *ogs_pfcp_pdr_find_or_add(
return pdr;
}
static uint64_t pdr_hash_keygen(uint32_t teid, uint8_t qfi)
{
uint64_t hashkey = (teid << 8) + qfi;
return hashkey;
}
void ogs_pfcp_pdr_hash_set(ogs_pfcp_pdr_t *pdr)
{
ogs_assert(pdr);
if (pdr->hashkey)
ogs_hash_set(ogs_pfcp_self()->pdr_hash,
&pdr->hashkey, sizeof(pdr->hashkey), NULL);
pdr->hashkey = pdr_hash_keygen(pdr->f_teid.teid, pdr->qfi);
ogs_hash_set(ogs_pfcp_self()->pdr_hash,
&pdr->hashkey, sizeof(pdr->hashkey), pdr);
}
ogs_pfcp_pdr_t *ogs_pfcp_pdr_find_by_teid_and_qfi(uint32_t teid, uint8_t qfi)
{
uint64_t hashkey = pdr_hash_keygen(teid, qfi);
return (ogs_pfcp_pdr_t *)ogs_hash_get(self.pdr_hash,
&hashkey, sizeof(hashkey));
}
void ogs_pfcp_pdr_reorder_by_precedence(
ogs_pfcp_pdr_t *pdr, ogs_pfcp_precedence_t precedence)
{
@ -947,8 +950,8 @@ void ogs_pfcp_pdr_remove(ogs_pfcp_pdr_t *pdr)
ogs_pfcp_rule_remove_all(pdr);
if (pdr->hashkey)
ogs_hash_set(ogs_pfcp_self()->pdr_hash, &pdr->hashkey,
sizeof(pdr->hashkey), NULL);
ogs_hash_set(ogs_pfcp_self()->pdr_hash,
&pdr->hashkey, sizeof(pdr->hashkey), NULL);
if (pdr->dnn)
ogs_free(pdr->dnn);
@ -1021,6 +1024,107 @@ ogs_pfcp_far_t *ogs_pfcp_far_find_or_add(
return far;
}
void ogs_pfcp_far_hash_set(ogs_pfcp_far_t *far)
{
int family;
ogs_gtp_node_t *gnode = NULL;
ogs_sockaddr_t *addr = NULL;
ogs_assert(far);
gnode = far->gnode;
ogs_assert(gnode);
addr = &gnode->addr;
ogs_assert(addr);
if (far->hashkey_len)
ogs_hash_set(ogs_pfcp_self()->far_hash,
&far->hashkey, far->hashkey_len, NULL);
far->hashkey.teid = far->outer_header_creation.teid;
far->hashkey_len = sizeof(far->hashkey.teid);
family = addr->ogs_sa_family;
switch (family) {
case AF_INET:
memcpy(far->hashkey.addr, &addr->sin.sin_addr, OGS_IPV4_LEN);
far->hashkey_len += OGS_IPV4_LEN;
break;
case AF_INET6:
memcpy(far->hashkey.addr, &addr->sin6.sin6_addr, OGS_IPV6_LEN);
far->hashkey_len += OGS_IPV6_LEN;
break;
default:
ogs_fatal("Unknown family(%d)", family);
ogs_abort();
return;
}
ogs_hash_set(ogs_pfcp_self()->far_hash,
&far->hashkey, far->hashkey_len, far);
}
ogs_pfcp_far_t *ogs_pfcp_far_find_by_error_indication(ogs_pkbuf_t *pkbuf)
{
ogs_pfcp_far_hashkey_t hashkey;
int hashkey_len;
uint32_t teid;
uint16_t len;
unsigned char *p = NULL;
ogs_assert(pkbuf);
p = pkbuf->data;
ogs_assert(p);
/*
* 8.3 Tunnel Endpoint Identifier Data I
*
* Octet 1 : Type = 16 (Decimal)
* Octet 2-5 : Tunnel Endpoint Identitifer Data I
*/
if (*p != 16) {
ogs_error("Unknown Type [%d]", *p);
return NULL;
}
p += 1;
memcpy(&teid, p, 4);
teid = be32toh(teid);
p += 4;
/*
* 8.4 GTP-U Peer Address
*
* Octet 1 : Type = 133 (Decimal)
* Octet 2-3 : Length
* Octet 4-n : IPv4 or IPv6 Address
*/
if (*p != 133) {
ogs_error("Unknown Type [%d]", *p);
return NULL;
}
p += 1;
memcpy(&len, p, 2);
len = be16toh(len);
p += 2;
if (len == OGS_IPV4_LEN) {
} else if (len == OGS_IPV6_LEN) {
} else {
ogs_error("Invalid Length [%d]", len);
return NULL;
}
hashkey.teid = teid;
memcpy(hashkey.addr, p, len);
hashkey_len = 4 + len;
return (ogs_pfcp_far_t *)ogs_hash_get(self.far_hash, &hashkey, hashkey_len);
}
void ogs_pfcp_far_remove(ogs_pfcp_far_t *far)
{
int i;
@ -1030,11 +1134,15 @@ void ogs_pfcp_far_remove(ogs_pfcp_far_t *far)
sess = far->sess;
ogs_assert(sess);
ogs_list_remove(&sess->far_list, far);
if (far->hashkey_len)
ogs_hash_set(ogs_pfcp_self()->far_hash,
&far->hashkey, far->hashkey_len, NULL);
for (i = 0; i < far->num_of_buffered_packet; i++)
ogs_pkbuf_free(far->buffered_packet[i]);
ogs_list_remove(&sess->far_list, far);
if (far->id_node)
ogs_pool_free(&far->sess->far_id_pool, far->id_node);

View File

@ -60,7 +60,8 @@ typedef struct ogs_pfcp_context_s {
ogs_list_t dev_list; /* Tun Device List */
ogs_list_t subnet_list; /* UE Subnet List */
ogs_hash_t *pdr_hash; /* hash table (UPF-N3-TEID) */
ogs_hash_t *pdr_hash; /* hash table for PDR(TEID+QFI) */
ogs_hash_t *far_hash; /* hash table for FAR(TEID+ADDR) */
} ogs_pfcp_context_t;
#define OGS_SETUP_PFCP_NODE(__cTX, __pNODE) \
@ -152,10 +153,18 @@ typedef struct ogs_pfcp_pdr_s {
ogs_pfcp_sess_t *sess;
} ogs_pfcp_pdr_t;
typedef struct ogs_pfcp_far_hashkey_s {
uint32_t teid;
uint32_t addr[4];
} ogs_pfcp_far_hashkey_t;
typedef struct ogs_pfcp_far_s {
ogs_lnode_t lnode;
uint32_t index;
int hashkey_len;
ogs_pfcp_far_hashkey_t hashkey;
uint8_t *id_node; /* Pool-Node for ID */
ogs_pfcp_far_id_t id;
ogs_pfcp_apply_action_t apply_action;
@ -166,7 +175,7 @@ typedef struct ogs_pfcp_far_s {
ogs_pfcp_smreq_flags_t smreq_flags;
uint32_t num_of_buffered_packet;
ogs_pkbuf_t* buffered_packet[OGS_MAX_NUM_OF_PACKET_BUFFER];
ogs_pkbuf_t *buffered_packet[OGS_MAX_NUM_OF_PACKET_BUFFER];
/* Related Context */
ogs_pfcp_sess_t *sess;
@ -304,13 +313,14 @@ ogs_pfcp_pdr_t *ogs_pfcp_sess_default_pdr(
void ogs_pfcp_sess_clear(ogs_pfcp_sess_t *sess);
ogs_pfcp_pdr_t *ogs_pfcp_pdr_add(ogs_pfcp_sess_t *sess);
void ogs_pfcp_pdr_hash_set(ogs_pfcp_pdr_t *pdr);
ogs_pfcp_pdr_t *ogs_pfcp_pdr_find(
ogs_pfcp_sess_t *sess, ogs_pfcp_pdr_id_t id);
ogs_pfcp_pdr_t *ogs_pfcp_pdr_find_by_teid_and_qfi(uint32_t teid, uint8_t qfi);
ogs_pfcp_pdr_t *ogs_pfcp_pdr_find_or_add(
ogs_pfcp_sess_t *sess, ogs_pfcp_pdr_id_t id);
void ogs_pfcp_pdr_hash_set(ogs_pfcp_pdr_t *pdr);
ogs_pfcp_pdr_t *ogs_pfcp_pdr_find_by_teid_and_qfi(uint32_t teid, uint8_t qfi);
void ogs_pfcp_pdr_reorder_by_precedence(
ogs_pfcp_pdr_t *pdr, ogs_pfcp_precedence_t precedence);
void ogs_pfcp_pdr_associate_far(ogs_pfcp_pdr_t *pdr, ogs_pfcp_far_t *far);
@ -324,6 +334,10 @@ ogs_pfcp_far_t *ogs_pfcp_far_find(
ogs_pfcp_sess_t *sess, ogs_pfcp_far_id_t id);
ogs_pfcp_far_t *ogs_pfcp_far_find_or_add(
ogs_pfcp_sess_t *sess, ogs_pfcp_far_id_t id);
void ogs_pfcp_far_hash_set(ogs_pfcp_far_t *far);
ogs_pfcp_far_t *ogs_pfcp_far_find_by_error_indication(ogs_pkbuf_t *pkbuf);
void ogs_pfcp_far_remove(ogs_pfcp_far_t *far);
void ogs_pfcp_far_remove_all(ogs_pfcp_sess_t *sess);

View File

@ -172,6 +172,38 @@ void ogs_pfcp_up_handle_pdr(
}
}
void ogs_pfcp_up_handle_error_indication(
ogs_pfcp_far_t *far, ogs_pfcp_user_plane_report_t *report)
{
uint16_t len;
ogs_assert(far);
ogs_assert(far->hashkey_len);
ogs_assert(report);
memset(report, 0, sizeof(*report));
len = far->hashkey_len - 4; /* Remove TEID size, Only use ADDR size */
report->error_indication.remote_f_teid_len = 5 + len;
report->error_indication.remote_f_teid.teid = htobe32(far->hashkey.teid);
if (len == OGS_IPV4_LEN) {
report->error_indication.remote_f_teid.ipv4 = 1;
memcpy(&report->error_indication.remote_f_teid.addr,
far->hashkey.addr, len);
} else if (len == OGS_IPV6_LEN) {
report->error_indication.remote_f_teid.ipv6 = 1;
memcpy(report->error_indication.remote_f_teid.addr6,
far->hashkey.addr, len);
} else {
ogs_error("Invalid Length [%d]", len);
return;
}
report->type.error_indication_report = 1;
}
ogs_pfcp_pdr_t *ogs_pfcp_handle_create_pdr(ogs_pfcp_sess_t *sess,
ogs_pfcp_tlv_create_pdr_t *message,
uint8_t *cause_value, uint8_t *offending_ie_value)

View File

@ -48,6 +48,8 @@ void ogs_pfcp_up_handle_association_setup_response(
void ogs_pfcp_up_handle_pdr(
ogs_pfcp_pdr_t *pdr, ogs_pkbuf_t *recvbuf,
ogs_pfcp_user_plane_report_t *report);
void ogs_pfcp_up_handle_error_indication(
ogs_pfcp_far_t *far, ogs_pfcp_user_plane_report_t *report);
ogs_pfcp_pdr_t *ogs_pfcp_handle_create_pdr(ogs_pfcp_sess_t *sess,
ogs_pfcp_tlv_create_pdr_t *message,

View File

@ -262,14 +262,11 @@ void ogs_pfcp_up_send_association_setup_response(ogs_pfcp_xact_t *xact,
void ogs_pfcp_send_g_pdu(ogs_pfcp_pdr_t *pdr, ogs_pkbuf_t *sendbuf)
{
char buf[OGS_ADDRSTRLEN];
int rv;
ogs_gtp_header_t *gtp_h = NULL;
ogs_gtp_extension_header_t *ext_h = NULL;
ogs_gtp_node_t *gnode = NULL;
ogs_pfcp_far_t *far = NULL;
ogs_pfcp_qer_t *qer = NULL;
ogs_gtp_header_t gtp_hdesc;
ogs_gtp_extension_header_t ext_hdesc;
ogs_assert(pdr);
ogs_assert(sendbuf);
@ -285,73 +282,26 @@ void ogs_pfcp_send_g_pdu(ogs_pfcp_pdr_t *pdr, ogs_pkbuf_t *sendbuf)
ogs_assert(gnode);
ogs_assert(gnode->sock);
qer = pdr->qer;
memset(&gtp_hdesc, 0, sizeof(gtp_hdesc));
memset(&ext_hdesc, 0, sizeof(ext_hdesc));
/* Add GTP-U header */
if (qer && qer->qfi) {
ogs_assert(ogs_pkbuf_push(sendbuf, OGS_GTPV1U_5GC_HEADER_LEN));
gtp_h = (ogs_gtp_header_t *)sendbuf->data;
/* Bits 8 7 6 5 4 3 2 1
* +--+--+--+--+--+--+--+--+
* |version |PT| 1| E| S|PN|
* +--+--+--+--+--+--+--+--+
* 0 0 1 1 0 1 0 0
*/
gtp_h->flags = 0x34;
gtp_h->type = OGS_GTPU_MSGTYPE_GPDU;
gtp_h->length = htobe16(sendbuf->len - OGS_GTPV1U_HEADER_LEN);
gtp_h->teid = htobe32(far->outer_header_creation.teid);
gtp_hdesc.type = OGS_GTPU_MSGTYPE_GPDU;
gtp_hdesc.teid = far->outer_header_creation.teid;
if (pdr->qer && pdr->qer->qfi)
ext_hdesc.qos_flow_identifier = pdr->qer->qfi;
ext_h = (ogs_gtp_extension_header_t *)(
sendbuf->data + OGS_GTPV1U_HEADER_LEN);
ext_h->type = OGS_GTP_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER;
ext_h->len = 1;
ext_h->pdu_type =
OGS_GTP_EXTENSION_HEADER_PDU_TYPE_DL_PDU_SESSION_INFORMATION;
ext_h->qos_flow_identifier = qer->qfi;
ext_h->next_type =
OGS_GTP_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS;
} else {
ogs_assert(ogs_pkbuf_push(sendbuf, OGS_GTPV1U_HEADER_LEN));
gtp_h = (ogs_gtp_header_t *)sendbuf->data;
/* Bits 8 7 6 5 4 3 2 1
* +--+--+--+--+--+--+--+--+
* |version |PT| 1| E| S|PN|
* +--+--+--+--+--+--+--+--+
* 0 0 1 1 0 0 0 0
*/
gtp_h->flags = 0x30;
gtp_h->type = OGS_GTPU_MSGTYPE_GPDU;
gtp_h->length = htobe16(sendbuf->len - OGS_GTPV1U_HEADER_LEN);
gtp_h->teid = htobe32(far->outer_header_creation.teid);
}
/* Send G-PDU */
ogs_debug("SEND G-PDU to Peer[%s] : TEID[0x%x]",
OGS_ADDR(&gnode->addr, buf), far->outer_header_creation.teid);
rv = ogs_gtp_sendto(gnode, sendbuf);
if (rv != OGS_OK) {
if (ogs_socket_errno != OGS_EAGAIN) {
ogs_error("SEND G-PDU to Peer[%s] : TEID[0x%x]",
OGS_ADDR(&gnode->addr, buf), far->outer_header_creation.teid);
}
}
ogs_pkbuf_free(sendbuf);
ogs_gtp_send_user_plane(gnode, &gtp_hdesc, &ext_hdesc, sendbuf);
}
void ogs_pfcp_send_end_marker(ogs_pfcp_pdr_t *pdr)
{
char buf[OGS_ADDRSTRLEN];
int rv;
ogs_gtp_node_t *gnode = NULL;
ogs_pfcp_far_t *far = NULL;
ogs_pkbuf_t *sendbuf = NULL;
ogs_gtp_header_t *gtp_h = NULL;
ogs_gtp_extension_header_t *ext_h = NULL;
ogs_gtp_node_t *gnode = NULL;
ogs_pfcp_far_t *far = NULL;
ogs_pfcp_qer_t *qer = NULL;
ogs_gtp_header_t gtp_hdesc;
ogs_gtp_extension_header_t ext_hdesc;
ogs_assert(pdr);
far = pdr->far;
@ -367,61 +317,19 @@ void ogs_pfcp_send_end_marker(ogs_pfcp_pdr_t *pdr)
return;
}
sendbuf = ogs_pkbuf_alloc(NULL,
100 /* enough for END_MARKER; use smaller buffer */);
sendbuf = ogs_pkbuf_alloc(NULL, OGS_GTPV1U_5GC_HEADER_LEN);
ogs_assert(sendbuf);
ogs_pkbuf_put(sendbuf, 100);
memset(sendbuf->data, 0, sendbuf->len);
ogs_pkbuf_reserve(sendbuf, OGS_GTPV1U_5GC_HEADER_LEN);
gtp_h = (ogs_gtp_header_t *)sendbuf->data;
memset(&gtp_hdesc, 0, sizeof(gtp_hdesc));
memset(&ext_hdesc, 0, sizeof(ext_hdesc));
qer = pdr->qer;
gtp_hdesc.type = OGS_GTPU_MSGTYPE_END_MARKER;
gtp_hdesc.teid = far->outer_header_creation.teid;
if (pdr->qer && pdr->qer->qfi)
ext_hdesc.qos_flow_identifier = pdr->qer->qfi;
/* Add GTP-U header */
if (qer && qer->qfi) {
/* Bits 8 7 6 5 4 3 2 1
* +--+--+--+--+--+--+--+--+
* |version |PT| 1| E| S|PN|
* +--+--+--+--+--+--+--+--+
* 0 0 1 1 0 1 0 0
*/
gtp_h->flags = 0x34;
gtp_h->type = OGS_GTPU_MSGTYPE_END_MARKER;
gtp_h->teid = htobe32(far->outer_header_creation.teid);
ext_h = (ogs_gtp_extension_header_t *)(
sendbuf->data + OGS_GTPV1U_HEADER_LEN);
ext_h->type = OGS_GTP_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER;
ext_h->len = 1;
ext_h->pdu_type =
OGS_GTP_EXTENSION_HEADER_PDU_TYPE_DL_PDU_SESSION_INFORMATION;
ext_h->qos_flow_identifier = qer->qfi;
ext_h->next_type =
OGS_GTP_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS;
} else {
/* Bits 8 7 6 5 4 3 2 1
* +--+--+--+--+--+--+--+--+
* |version |PT| 1| E| S|PN|
* +--+--+--+--+--+--+--+--+
* 0 0 1 1 0 0 0 0
*/
gtp_h->flags = 0x30;
gtp_h->type = OGS_GTPU_MSGTYPE_END_MARKER;
gtp_h->teid = htobe32(far->outer_header_creation.teid);
}
/* Send End Marker */
ogs_debug("SEND End Marker to Peer[%s] : TEID[0x%x]",
OGS_ADDR(&gnode->addr, buf), far->outer_header_creation.teid);
rv = ogs_gtp_sendto(gnode, sendbuf);
if (rv != OGS_OK) {
if (ogs_socket_errno != OGS_EAGAIN) {
ogs_error("SEND End Marker to Peer[%s] : TEID[0x%x]",
OGS_ADDR(&gnode->addr, buf), far->outer_header_creation.teid);
}
}
ogs_pkbuf_free(sendbuf);
ogs_gtp_send_user_plane(gnode, &gtp_hdesc, &ext_hdesc, sendbuf);
}
void ogs_pfcp_send_buffered_packet(ogs_pfcp_pdr_t *pdr)

View File

@ -79,6 +79,7 @@ typedef struct ogs_pfcp_xact_s {
#define OGS_PFCP_MODIFY_ACTIVATE ((uint64_t)1<<8)
#define OGS_PFCP_MODIFY_DEACTIVATE ((uint64_t)1<<9)
#define OGS_PFCP_MODIFY_END_MARKER ((uint64_t)1<<10)
#define OGS_PFCP_MODIFY_ERROR_INDICATION ((uint64_t)1<<11)
uint64_t modify_flags;
#define OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED 1

View File

@ -34,11 +34,11 @@ libsctp_sources = files('''
ogs-sctp.c
'''.split())
libsctp_dep = cc.find_library('sctp', required : false)
if libsctp_dep.found()
sctp_dep = cc.find_library('sctp', required : false)
if sctp_dep.found()
libsctp_sources += files('ogs-lksctp.c')
else
libsctp_dep = dependency('usrsctp',
sctp_dep = dependency('usrsctp',
version: ['>=1.0.0', '<2'],
fallback: ['usrsctp', 'usrsctp_dep'],
default_options: [
@ -59,10 +59,10 @@ libsctp = library('ogssctp',
version : libogslib_version,
c_args : '-DOGS_SCTP_COMPILATION',
include_directories : [libsctp_inc, libinc],
dependencies : [libcore_dep, libsctp_dep],
dependencies : [libcore_dep, sctp_dep],
install : true)
libsctp_dep = declare_dependency(
link_with : libsctp,
include_directories : [libsctp_inc, libinc],
dependencies : [libcore_dep, libsctp_dep])
dependencies : [libcore_dep, sctp_dep])

View File

@ -145,7 +145,10 @@ int esm_handle_information_response(mme_sess_t *sess,
} else if (sess->pdn->paa.pdn_type == OGS_GTP_PDN_TYPE_IPV4V6) {
/* Nothing */
} else {
ogs_error("Unknown PDN Type %u", sess->pdn->paa.pdn_type);
ogs_error("Unknown PDN[%s] Type %u:%u",
sess->pdn->apn,
sess->pdn->pdn_type,
sess->pdn->paa.pdn_type);
nas_eps_send_pdn_connectivity_reject(
sess, ESM_CAUSE_UNKNOWN_PDN_TYPE);
return OGS_ERROR;

View File

@ -2533,8 +2533,8 @@ int mme_ue_set_imsi(mme_ue_t *mme_ue, char *imsi_bcd)
ogs_warn("[%s] OLD UE Context Release", mme_ue->imsi_bcd);
if (ECM_CONNECTED(old_mme_ue)) {
/* Implcit S1 release */
ogs_debug("[%s] Implicit S1 release", mme_ue->imsi_bcd);
ogs_debug("[%s] ENB_UE_S1AP_ID[%d] MME_UE_S1AP_ID[%d]",
ogs_info("[%s] Implicit S1 release", mme_ue->imsi_bcd);
ogs_info("[%s] ENB_UE_S1AP_ID[%d] MME_UE_S1AP_ID[%d]",
old_mme_ue->imsi_bcd,
old_mme_ue->enb_ue->enb_ue_s1ap_id,
old_mme_ue->enb_ue->mme_ue_s1ap_id);

View File

@ -264,6 +264,7 @@ struct enb_ue_s {
#define S1AP_UE_CTX_REL_S1_REMOVE_AND_UNLINK 2
#define S1AP_UE_CTX_REL_UE_CONTEXT_REMOVE 3
#define S1AP_UE_CTX_REL_DELETE_INDIRECT_TUNNEL 4
#define S1AP_UE_CTX_REL_S1_PAGING 5
uint8_t ue_ctx_rel_action;
/* Related Context */

View File

@ -100,31 +100,14 @@ void mme_s11_handle_create_session_response(
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
/* CHECK PDN_TYPE */
if (rsp->pdn_address_allocation.presence) {
ogs_paa_t paa;
if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED)
ogs_warn("Cause[%d] : No Accepted", cause_value);
memcpy(&paa, rsp->pdn_address_allocation.data,
ogs_min(sizeof(paa), rsp->pdn_address_allocation.len));
if (paa.pdn_type == OGS_GTP_PDN_TYPE_IPV4) {
/* Nothing */
} else if (paa.pdn_type == OGS_GTP_PDN_TYPE_IPV6) {
/* Nothing */
} else if (paa.pdn_type == OGS_GTP_PDN_TYPE_IPV4V6) {
/* Nothing */
} else {
ogs_error("Unknown PDN Type %u", paa.pdn_type);
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_INCORRECT;
}
} else {
if (rsp->pdn_address_allocation.presence == 0) {
ogs_error("No PDN Address Allocation");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED)
ogs_warn("Cause[%d] : No Accepted", cause_value);
if (rsp->sender_f_teid_for_control_plane.presence == 0) {
ogs_error("No S11 TEID");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
@ -728,7 +711,7 @@ void mme_s11_handle_downlink_data_notification(
ogs_gtp_downlink_data_notification_t *noti)
{
int rv;
ogs_gtp_cause_t cause;
uint8_t cause_value = 0;
ogs_gtp_header_t h;
ogs_pkbuf_t *s11buf = NULL;
@ -737,21 +720,15 @@ void mme_s11_handle_downlink_data_notification(
ogs_debug("[MME] Downlink Data Notification");
memset(&cause, 0, sizeof(cause));
cause.value = OGS_GTP_CAUSE_REQUEST_ACCEPTED;
if (!mme_ue) {
ogs_warn("OGS_GTP_CAUSE_CONTEXT_NOT_FOUND");
cause.value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND;
}
if (cause.value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) {
ogs_gtp_send_error_message(xact, mme_ue ? mme_ue->sgw_s11_teid : 0,
OGS_GTP_DOWNLINK_DATA_NOTIFICATION_ACKNOWLEDGE_TYPE,
cause.value);
OGS_GTP_CAUSE_CONTEXT_NOT_FOUND);
return;
}
ogs_assert(mme_ue);
ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]",
mme_ue->mme_s11_teid, mme_ue->sgw_s11_teid);
@ -768,6 +745,69 @@ void mme_s11_handle_downlink_data_notification(
rv = ogs_gtp_xact_commit(xact);
ogs_expect(rv == OGS_OK);
if (noti->cause.presence) {
ogs_gtp_cause_t *cause = noti->cause.data;
ogs_assert(cause);
cause_value = cause->value;
}
/*
* 5.3.4.2 in Spec 23.401
* Under certain conditions, the current UE triggered Service Request
* procedure can cause unnecessary Downlink Packet Notification messages
* which increase the load of the MME.
*
* This can occur when uplink data sent in step 6 causes a response
* on the downlink which arrives at the Serving GW before the Modify Bearer
* Request message, step 8. This data cannot be forwarded from the Serving GW
* to the eNodeB and hence it triggers a Downlink Data Notification message.
*
* If the MME receives a Downlink Data Notification after step 2 and
* before step 9, the MME shall not send S1 interface paging messages
*/
if (ECM_IDLE(mme_ue)) {
s1ap_send_paging(mme_ue, S1AP_CNDomain_ps);
} else if (ECM_CONNECTED(mme_ue)) {
if (cause_value == OGS_GTP_CAUSE_ERROR_INDICATION_RECEIVED) {
/*
* TS23.007 22. Downlink Data Notification Handling at MME/S4 SGSN
*
* If the MME/S4 SGSN receives a Downlink Data Notification message from
* the SGW as a result of the SGW having received an Error Indication message
* from the eNodeB/RNC or S4-SGSN over S4 User Plane, the MME/S4 SGSN should
* perform the following:
*
* - If the UE is in IDLE state, upon receipt of the Downlink Data
* Notification message, the MME/S4 SGSN shall perform the Network
* Triggered Service Request procedure as specified in 3GPP TS 23.060[5]
* and 3GPP TS 23.401 [15].
* - If the UE is in CONNECTED state, upon receipt of the Downlink Data
* Notification message, the MME shall perform S1 Release procedure and
* perform Network Triggered Service Request procedure as specified
* in 3GPP TS 23.401 [15].
* - If the UE is in CONNECTED state, upon receipt of the Downlink Data
* Notification message and Direct Tunnel is used, the S4-SGSN shall
* perform Iu Release procedure and perform Network Triggered Service
* Request procedure as specified in 3GPP TS 23.060 [5]
* if the cause value included in Downlink Data Notification is
* "Error Indication received from RNC/eNodeB/S4-SGSN",
* - If the UE is in CONNECTED state, upon receipt of the Downlink Data
* Notification message and Direct Tunnel is not used, the S4-SGSN should
* re-establish all of the S4-U bearers of this UE if the cause value
* included in Downlink Data Notification is "Error Indication received
* from RNC/eNodeB/S4-SGSN"
*/
enb_ue_t *enb_ue = mme_ue->enb_ue;
ogs_assert(enb_ue);
s1ap_send_ue_context_release_command(enb_ue,
S1AP_Cause_PR_nas, S1AP_CauseNas_normal_release,
S1AP_UE_CTX_REL_S1_PAGING, 0);
}
}
}
void mme_s11_handle_create_indirect_data_forwarding_tunnel_response(

View File

@ -609,23 +609,6 @@ void mme_state_operational(ogs_fsm_t *s, mme_event_t *e)
}
mme_s11_handle_downlink_data_notification(
xact, mme_ue, &gtp_message.downlink_data_notification);
/*
* 5.3.4.2 in Spec 23.401
* Under certain conditions, the current UE triggered Service Request
* procedure can cause unnecessary Downlink Packet Notification messages
* which increase the load of the MME.
*
* This can occur when uplink data sent in step 6 causes a response
* on the downlink which arrives at the Serving GW before the Modify Bearer
* Request message, step 8. This data cannot be forwarded from the Serving GW
* to the eNodeB and hence it triggers a Downlink Data Notification message.
*
* If the MME receives a Downlink Data Notification after step 2 and
* before step 9, the MME shall not send S1 interface paging messages
*/
if (ECM_IDLE(mme_ue))
s1ap_send_paging(mme_ue, S1AP_CNDomain_ps);
break;
case OGS_GTP_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE_TYPE:
mme_s11_handle_create_indirect_data_forwarding_tunnel_response(

View File

@ -684,18 +684,7 @@ void s1ap_handle_initial_context_setup_failure(
* may in principle be adopted. The eNB should ensure
* that no hanging resources remain at the eNB.
*/
#if 0
if (mme_ue && SESSION_CONTEXT_IS_AVAILABLE(mme_ue)) {
mme_gtp_send_delete_all_sessions(mme_ue);
} else {
s1ap_send_ue_context_release_command(enb_ue,
S1AP_Cause_PR_nas, S1AP_CauseNas_normal_release,
S1AP_UE_CTX_REL_S1_CONTEXT_REMOVE, 0);
}
#else
/* In Issues #568, v2.0.12 is not working. */
mme_send_release_access_bearer_or_ue_context_release(enb_ue);
#endif
}
void s1ap_handle_ue_context_modification_response(
@ -1166,6 +1155,14 @@ void s1ap_handle_ue_context_release_action(enb_ue_t *enb_ue)
ogs_expect(rv == OGS_OK);
}
break;
case S1AP_UE_CTX_REL_S1_PAGING:
ogs_debug(" Action: S1 paging");
enb_ue_remove(enb_ue);
ogs_expect_or_return(mme_ue);
mme_ue_deassociate(mme_ue);
s1ap_send_paging(mme_ue, S1AP_CNDomain_ps);
break;
default:
ogs_error("Invalid Action[%d]", enb_ue->ue_ctx_rel_action);
break;

View File

@ -677,6 +677,71 @@ sgwc_bearer_t *sgwc_bearer_find_by_ue_ebi(sgwc_ue_t *sgwc_ue, uint8_t ebi)
return NULL;
}
sgwc_bearer_t *sgwc_bearer_find_by_error_indication_report(
sgwc_sess_t *sess,
ogs_pfcp_tlv_error_indication_report_t *error_indication_report)
{
ogs_pfcp_f_teid_t *remote_f_teid;
sgwc_bearer_t *bearer = NULL;
sgwc_tunnel_t *tunnel = NULL;
uint32_t teid;
uint16_t len; /* OGS_IPV4_LEN or OGS_IPV6_LEN */
uint32_t addr[4];
ogs_assert(sess);
ogs_assert(error_indication_report);
if (error_indication_report->presence == 0) {
ogs_error("No Error Indication Report");
return NULL;
}
if (error_indication_report->remote_f_teid.presence == 0) {
ogs_error("No Remote F-TEID");
return NULL;
}
remote_f_teid = error_indication_report->remote_f_teid.data;
ogs_assert(remote_f_teid);
teid = be32toh(remote_f_teid->teid);
if (remote_f_teid->ipv4 && remote_f_teid->ipv6) {
ogs_error("User plane should not set both IPv4 and IPv6");
return NULL;
} else if (remote_f_teid->ipv4) {
len = OGS_IPV4_LEN;
memcpy(addr, &remote_f_teid->addr, len);
} else if (remote_f_teid->ipv6) {
len = OGS_IPV6_LEN;
memcpy(addr, remote_f_teid->addr6, len);
} else {
ogs_error("No IPv4 and IPv6");
return NULL;
}
ogs_list_for_each(&sess->bearer_list, bearer) {
ogs_list_for_each(&bearer->tunnel_list, tunnel) {
if (teid == tunnel->remote_teid) {
if (len == OGS_IPV4_LEN && tunnel->remote_ip.ipv4 &&
memcmp(addr, &tunnel->remote_ip.addr, len) == 0) {
return bearer;
} else if (len == OGS_IPV6_LEN && tunnel->remote_ip.ipv6 &&
memcmp(addr, tunnel->remote_ip.addr6, len) == 0) {
return bearer;
}
}
}
}
ogs_error("Cannot find the bearer context "
"[TEID:%d,LEN:%d,ADDR:%08x %08x %08x %08x]",
teid, len, be32toh(addr[0]), be32toh(addr[1]),
be32toh(addr[2]), be32toh(addr[3]));
return NULL;
}
sgwc_bearer_t *sgwc_default_bearer_in_sess(sgwc_sess_t *sess)
{
ogs_assert(sess);

View File

@ -185,6 +185,9 @@ sgwc_bearer_t *sgwc_bearer_find_by_sess_ebi(
sgwc_sess_t *sess, uint8_t ebi);
sgwc_bearer_t *sgwc_bearer_find_by_ue_ebi(
sgwc_ue_t *sgwc_ue, uint8_t ebi);
sgwc_bearer_t *sgwc_bearer_find_by_error_indication_report(
sgwc_sess_t *sess,
ogs_pfcp_tlv_error_indication_report_t *error_indication_report);
sgwc_bearer_t *sgwc_default_bearer_in_sess(sgwc_sess_t *sess);
sgwc_tunnel_t *sgwc_tunnel_add(

View File

@ -140,7 +140,7 @@ void sgwc_gtp_close(void)
}
void sgwc_gtp_send_downlink_data_notification(
sgwc_bearer_t *bearer, ogs_pfcp_xact_t *pfcp_xact)
uint8_t cause_value, sgwc_bearer_t *bearer)
{
int rv;
@ -153,7 +153,6 @@ void sgwc_gtp_send_downlink_data_notification(
ogs_gtp_header_t h;
ogs_assert(bearer);
ogs_assert(pfcp_xact);
sess = bearer->sess;
ogs_assert(sess);
@ -169,13 +168,11 @@ void sgwc_gtp_send_downlink_data_notification(
h.type = OGS_GTP_DOWNLINK_DATA_NOTIFICATION_TYPE;
h.teid = sgwc_ue->mme_s11_teid;
pkbuf = sgwc_s11_build_downlink_data_notification(bearer);
pkbuf = sgwc_s11_build_downlink_data_notification(cause_value, bearer);
ogs_expect_or_return(pkbuf);
gtp_xact = ogs_gtp_xact_local_create(
sgwc_ue->gnode, &h, pkbuf, NULL, sess);
gtp_xact = ogs_gtp_xact_local_create(sgwc_ue->gnode, &h, pkbuf, NULL, sess);
ogs_expect_or_return(gtp_xact);
gtp_xact->pfcp_xact = pfcp_xact;
rv = ogs_gtp_xact_commit(gtp_xact);
ogs_expect(rv == OGS_OK);

View File

@ -30,7 +30,7 @@ int sgwc_gtp_open(void);
void sgwc_gtp_close(void);
void sgwc_gtp_send_downlink_data_notification(
sgwc_bearer_t *bearer, ogs_pfcp_xact_t *pfcp_xact);
uint8_t cause_value, sgwc_bearer_t *bearer);
#ifdef __cplusplus
}

View File

@ -18,9 +18,6 @@
*/
#include "pfcp-path.h"
#if 0
#include "n4-build.h"
#endif
static void pfcp_node_fsm_init(ogs_pfcp_node_t *node, bool try_to_assoicate)
{

View File

@ -19,10 +19,12 @@
#include "s11-build.h"
ogs_pkbuf_t *sgwc_s11_build_downlink_data_notification(sgwc_bearer_t *bearer)
ogs_pkbuf_t *sgwc_s11_build_downlink_data_notification(
uint8_t cause_value, sgwc_bearer_t *bearer)
{
ogs_gtp_message_t message;
ogs_gtp_downlink_data_notification_t *noti = NULL;
ogs_gtp_cause_t cause;
ogs_gtp_arp_t arp;
sgwc_sess_t *sess = NULL;
@ -34,6 +36,20 @@ ogs_pkbuf_t *sgwc_s11_build_downlink_data_notification(sgwc_bearer_t *bearer)
noti = &message.downlink_data_notification;
memset(&message, 0, sizeof(ogs_gtp_message_t));
/*
* TS29.274 8.4 Cause Value
*
* 0 : Reserved. Shall not be sent and
* if received the Cause shall be treated as an invalid IE
*/
if (cause_value != OGS_GTP_CAUSE_INVALID_VALUE) {
memset(&cause, 0, sizeof(cause));
cause.value = cause_value;
noti->cause.presence = 1;
noti->cause.len = sizeof(cause);
noti->cause.data = &cause;
}
noti->eps_bearer_id.presence = 1;
noti->eps_bearer_id.u8 = bearer->ebi;

View File

@ -26,7 +26,8 @@
extern "C" {
#endif
ogs_pkbuf_t *sgwc_s11_build_downlink_data_notification(sgwc_bearer_t *bearer);
ogs_pkbuf_t *sgwc_s11_build_downlink_data_notification(
uint8_t cause_value, sgwc_bearer_t *bearer);
#ifdef __cplusplus
}

View File

@ -39,6 +39,8 @@ static void timeout(ogs_gtp_xact_t *xact, void *data)
sgwc_ue->imsi_bcd, type);
}
/* This code was created in case it will be used later,
* and is currently not being used. */
static uint8_t pfcp_cause_from_gtp(uint8_t gtp_cause)
{
switch (gtp_cause) {
@ -820,7 +822,6 @@ void sgwc_s11_handle_downlink_data_notification_ack(
uint8_t cause_value;
sgwc_sess_t *sess = NULL;
ogs_pfcp_xact_t *pfcp_xact;
ogs_gtp_downlink_data_notification_acknowledge_t *ack = NULL;
@ -829,8 +830,6 @@ void sgwc_s11_handle_downlink_data_notification_ack(
ack = &message->downlink_data_notification_acknowledge;
ogs_assert(ack);
pfcp_xact = s11_xact->pfcp_xact;
ogs_assert(pfcp_xact);
sess = s11_xact->data;
ogs_assert(sess);
sgwc_ue = sess->sgwc_ue;
@ -844,24 +843,16 @@ void sgwc_s11_handle_downlink_data_notification_ack(
ogs_assert(cause);
cause_value = cause->value;
if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED)
ogs_warn("GTP Failed [CAUSE:%d] - PFCP_CAUSE[%d]",
cause_value, pfcp_cause_from_gtp(cause_value));
} else {
ogs_error("No Cause");
cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING;
}
if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) {
ogs_pfcp_send_error_message(pfcp_xact, sess ? sess->sgwu_sxa_seid : 0,
OGS_PFCP_SESSION_REPORT_RESPONSE_TYPE,
pfcp_cause_from_gtp(cause_value), 0);
return;
}
ogs_debug("Downlink Data Notification Acknowledge");
ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]",
sgwc_ue->mme_s11_teid, sgwc_ue->sgw_s11_teid);
sgwc_pfcp_send_session_report_response(
pfcp_xact, sess, pfcp_cause_from_gtp(cause_value));
}
void sgwc_s11_handle_create_indirect_data_forwarding_tunnel_request(

View File

@ -244,7 +244,6 @@ void sgwc_sxa_handle_session_modification_response(
ogs_assert(pfcp_xact);
ogs_assert(pfcp_rsp);
ogs_assert(recv_message);
flags = pfcp_xact->modify_flags;
ogs_assert(flags);
@ -343,6 +342,7 @@ void sgwc_sxa_handle_session_modification_response(
s5c_xact = pfcp_xact->assoc_xact;
ogs_assert(s5c_xact);
ogs_assert(recv_message);
gtp_req = &recv_message->create_bearer_request;
ogs_assert(gtp_req);
@ -384,6 +384,7 @@ void sgwc_sxa_handle_session_modification_response(
s5c_xact = pfcp_xact->assoc_xact;
ogs_assert(s5c_xact);
ogs_assert(recv_message);
gtp_rsp = &recv_message->create_bearer_response;
ogs_assert(gtp_rsp);
@ -451,6 +452,7 @@ void sgwc_sxa_handle_session_modification_response(
ogs_gtp_f_teid_t rsp_dl_teid[OGS_GTP_MAX_INDIRECT_TUNNEL];
ogs_gtp_f_teid_t rsp_ul_teid[OGS_GTP_MAX_INDIRECT_TUNNEL];
ogs_assert(recv_message);
gtp_req = &recv_message->
create_indirect_data_forwarding_tunnel_request;
ogs_assert(gtp_req);
@ -616,6 +618,7 @@ void sgwc_sxa_handle_session_modification_response(
s5c_xact = pfcp_xact->assoc_xact;
ogs_assert(s5c_xact);
ogs_assert(recv_message);
recv_message->h.type = OGS_GTP_DELETE_BEARER_RESPONSE_TYPE;
recv_message->h.teid = sess->pgw_s5c_teid;
@ -640,6 +643,7 @@ void sgwc_sxa_handle_session_modification_response(
ogs_gtp_f_teid_t sgw_s11_teid;
ogs_gtp_f_teid_t sgw_s1u_teid;
ogs_assert(recv_message);
gtp_rsp = &recv_message->create_session_response;
ogs_assert(gtp_rsp);
@ -684,6 +688,7 @@ void sgwc_sxa_handle_session_modification_response(
ogs_gtp_modify_bearer_request_t *gtp_req = NULL;
ogs_gtp_modify_bearer_response_t *gtp_rsp = NULL;
ogs_assert(recv_message);
gtp_req = &recv_message->modify_bearer_request;
ogs_assert(gtp_req);
@ -745,9 +750,6 @@ void sgwc_sxa_handle_session_modification_response(
} else if (flags & OGS_PFCP_MODIFY_DEACTIVATE) {
bool release_access_bearers_is_done;
s11_xact = pfcp_xact->assoc_xact;
ogs_assert(s11_xact);
sess->state.release_access_bearers = true;
release_access_bearers_is_done = true;
@ -757,31 +759,45 @@ void sgwc_sxa_handle_session_modification_response(
}
if (release_access_bearers_is_done == true) {
ogs_gtp_release_access_bearers_response_t *gtp_rsp = NULL;
if (flags & OGS_PFCP_MODIFY_ERROR_INDICATION) {
/* It's faked method for receiving `bearer` context */
bearer = pfcp_xact->assoc_xact;
ogs_assert(bearer);
gtp_rsp = &send_message.release_access_bearers_response;
ogs_assert(gtp_rsp);
sgwc_gtp_send_downlink_data_notification(
OGS_GTP_CAUSE_ERROR_INDICATION_RECEIVED, bearer);
memset(&send_message, 0, sizeof(ogs_gtp_message_t));
} else {
ogs_gtp_release_access_bearers_response_t *gtp_rsp = NULL;
memset(&cause, 0, sizeof(cause));
cause.value = OGS_GTP_CAUSE_REQUEST_ACCEPTED;
s11_xact = pfcp_xact->assoc_xact;
ogs_assert(s11_xact);
gtp_rsp->cause.presence = 1;
gtp_rsp->cause.data = &cause;
gtp_rsp->cause.len = sizeof(cause);
gtp_rsp = &send_message.release_access_bearers_response;
ogs_assert(gtp_rsp);
send_message.h.type = OGS_GTP_RELEASE_ACCESS_BEARERS_RESPONSE_TYPE;
send_message.h.teid = sgwc_ue->mme_s11_teid;
memset(&send_message, 0, sizeof(ogs_gtp_message_t));
pkbuf = ogs_gtp_build_msg(&send_message);
ogs_expect_or_return(pkbuf);
memset(&cause, 0, sizeof(cause));
cause.value = OGS_GTP_CAUSE_REQUEST_ACCEPTED;
rv = ogs_gtp_xact_update_tx(s11_xact, &send_message.h, pkbuf);
ogs_expect_or_return(rv == OGS_OK);
gtp_rsp->cause.presence = 1;
gtp_rsp->cause.data = &cause;
gtp_rsp->cause.len = sizeof(cause);
rv = ogs_gtp_xact_commit(s11_xact);
ogs_expect(rv == OGS_OK);
send_message.h.type =
OGS_GTP_RELEASE_ACCESS_BEARERS_RESPONSE_TYPE;
send_message.h.teid = sgwc_ue->mme_s11_teid;
pkbuf = ogs_gtp_build_msg(&send_message);
ogs_expect_or_return(pkbuf);
rv = ogs_gtp_xact_update_tx(s11_xact, &send_message.h, pkbuf);
ogs_expect_or_return(rv == OGS_OK);
rv = ogs_gtp_xact_commit(s11_xact);
ogs_expect(rv == OGS_OK);
}
}
} else {
ogs_fatal("Invalid modify_flags[0x%llx]", (long long)flags);
@ -894,31 +910,65 @@ void sgwc_sxa_handle_session_report_request(
sgwc_ue = sess->sgwc_ue;
ogs_assert(sgwc_ue);
if (sgwc_ue->gnode) {
report_type.value = pfcp_req->report_type.u8;
if (report_type.downlink_data_report) {
if (pfcp_req->downlink_data_report.presence &&
pfcp_req->downlink_data_report.pdr_id.presence) {
if (!sgwc_ue->gnode) {
ogs_error("No SGWC-UE GTP Node");
ogs_pfcp_send_error_message(pfcp_xact, sess ? sess->sgwu_sxa_seid : 0,
OGS_PFCP_SESSION_REPORT_RESPONSE_TYPE,
OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND, 0);
return;
}
pdr_id = pfcp_req->downlink_data_report.pdr_id.u16;
sgwc_pfcp_send_session_report_response(
pfcp_xact, sess, OGS_PFCP_CAUSE_REQUEST_ACCEPTED);
ogs_list_for_each(&sess->bearer_list, bearer) {
ogs_list_for_each(&bearer->tunnel_list, tunnel) {
ogs_assert(tunnel->pdr);
if (tunnel->pdr->id == pdr_id) {
sgwc_gtp_send_downlink_data_notification(
bearer, pfcp_xact);
return;
}
}
report_type.value = pfcp_req->report_type.u8;
if (report_type.downlink_data_report) {
if (pfcp_req->downlink_data_report.presence == 0) {
ogs_error("No Downlink Data Report");
return;
}
if (pfcp_req->downlink_data_report.pdr_id.presence == 0) {
ogs_error("No PDR-ID");
return;
}
pdr_id = pfcp_req->downlink_data_report.pdr_id.u16;
ogs_list_for_each(&sess->bearer_list, bearer) {
ogs_list_for_each(&bearer->tunnel_list, tunnel) {
ogs_assert(tunnel->pdr);
if (tunnel->pdr->id == pdr_id) {
sgwc_gtp_send_downlink_data_notification(
OGS_GTP_CAUSE_INVALID_VALUE, bearer);
return;
}
}
}
ogs_error("Cannot find the PDR-ID[%d]", pdr_id);
} else if (report_type.error_indication_report) {
bearer = sgwc_bearer_find_by_error_indication_report(
sess, &pfcp_req->error_indication_report);
if (!bearer) return;
ogs_list_for_each(&sgwc_ue->sess_list, sess) {
sess->state.release_access_bearers = false;
sgwc_pfcp_send_sess_modification_request(sess,
/* We only use the `assoc_xact` parameter temporarily here
* to pass the `bearer` context. */
(ogs_gtp_xact_t *)bearer,
NULL,
OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE|
OGS_PFCP_MODIFY_ERROR_INDICATION);
}
} else {
ogs_error("Not supported Report Type[%d]", report_type.value);
}
ogs_error("No Bearer");
ogs_pfcp_send_error_message(pfcp_xact, 0,
OGS_PFCP_SESSION_REPORT_RESPONSE_TYPE,
OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND, 0);
}

View File

@ -30,16 +30,16 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data)
ssize_t size;
char buf[OGS_ADDRSTRLEN];
sgwu_sess_t *sess = NULL;
ogs_pkbuf_t *pkbuf = NULL;
ogs_sockaddr_t from;
ogs_gtp_header_t *gtp_h = NULL;
struct ip *ip_h = NULL;
ogs_pfcp_user_plane_report_t report;
uint32_t teid;
uint8_t qfi;
ogs_pfcp_pdr_t *pdr = NULL;
ogs_pfcp_user_plane_report_t report;
ogs_assert(fd != INVALID_SOCKET);
@ -89,25 +89,8 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data)
teid = be32toh(gtp_h->teid);
if (gtp_h->type == OGS_GTPU_MSGTYPE_END_MARKER) {
ogs_debug("[RECV] End Marker from [%s] : TEID[0x%x]",
OGS_ADDR(&from, buf), teid);
goto cleanup;
}
if (gtp_h->type == OGS_GTPU_MSGTYPE_ERR_IND) {
ogs_warn("[RECV] Error Indication from [%s]", OGS_ADDR(&from, buf));
goto cleanup;
}
if (gtp_h->type != OGS_GTPU_MSGTYPE_GPDU) {
ogs_error("[DROP] Invalid GTPU Type [%d]", gtp_h->type);
ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len);
goto cleanup;
}
ogs_debug("[RECV] GPU-U from [%s] : TEID[0x%x]",
OGS_ADDR(&from, buf), teid);
ogs_debug("[RECV] GPU-U Type [%d] from [%s] : TEID[0x%x]",
gtp_h->type, OGS_ADDR(&from, buf), teid);
qfi = 0;
if (gtp_h->flags & OGS_GTPU_FLAGS_E) {
@ -142,31 +125,53 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data)
}
ogs_assert(ogs_pkbuf_pull(pkbuf, len));
ip_h = (struct ip *)pkbuf->data;
ogs_assert(ip_h);
if (gtp_h->type == OGS_GTPU_MSGTYPE_END_MARKER) {
/* Nothing */
pdr = ogs_pfcp_pdr_find_by_teid_and_qfi(teid, qfi);
if (!pdr) {
#if 0 /* It's redundant log message */
ogs_warn("[DROP] Cannot find PDR : TEID[0x%x] QFI[%d]",
teid, qfi);
#endif
goto cleanup;
}
} else if (gtp_h->type == OGS_GTPU_MSGTYPE_ERR_IND) {
ogs_pfcp_far_t *far = NULL;
ogs_pfcp_up_handle_pdr(pdr, pkbuf, &report);
far = ogs_pfcp_far_find_by_error_indication(pkbuf);
if (far) {
ogs_pfcp_up_handle_error_indication(far, &report);
if (report.type.downlink_data_report) {
sgwu_sess_t *sess = NULL;
if (report.type.error_indication_report) {
ogs_assert(far->sess);
sess = SGWU_SESS(far->sess);
ogs_assert(sess);
ogs_assert(pdr->sess);
sess = SGWU_SESS(pdr->sess);
ogs_assert(sess);
sgwu_pfcp_send_session_report_request(sess, &report);
}
report.downlink_data.pdr_id = pdr->id;
report.downlink_data.qfi = qfi; /* for 5GC */
} else {
ogs_error("[DROP] Cannot decode Error-Indication");
ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len);
}
} else if (gtp_h->type == OGS_GTPU_MSGTYPE_GPDU) {
struct ip *ip_h = NULL;
ogs_pfcp_pdr_t *pdr = NULL;
sgwu_pfcp_send_session_report_request(sess, &report);
ip_h = (struct ip *)pkbuf->data;
ogs_assert(ip_h);
pdr = ogs_pfcp_pdr_find_by_teid_and_qfi(teid, qfi);
if (pdr) {
ogs_pfcp_up_handle_pdr(pdr, pkbuf, &report);
if (report.type.downlink_data_report) {
ogs_assert(pdr->sess);
sess = SGWU_SESS(pdr->sess);
ogs_assert(sess);
report.downlink_data.pdr_id = pdr->id;
report.downlink_data.qfi = qfi; /* for 5GC */
sgwu_pfcp_send_session_report_request(sess, &report);
}
}
} else {
ogs_error("[DROP] Invalid GTPU Type [%d]", gtp_h->type);
ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len);
}
cleanup:

View File

@ -47,7 +47,10 @@ static void setup_gtp_node(ogs_pfcp_far_t *far)
sgwu_self()->gtpu_sock, sgwu_self()->gtpu_sock6, gnode);
ogs_assert(rv == OGS_OK);
}
OGS_SETUP_GTP_NODE(far, gnode);
ogs_pfcp_far_hash_set(far);
}
void sgwu_sxa_handle_session_establishment_request(

View File

@ -85,7 +85,7 @@ static void _gtpv1_tun_recv_cb(short when, ogs_socket_t fd, void *data)
static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data)
{
int rv, len;
int len;
ssize_t size;
char buf[OGS_ADDRSTRLEN];
@ -93,14 +93,12 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data)
ogs_sockaddr_t from;
ogs_gtp_header_t *gtp_h = NULL;
struct ip *ip_h = NULL;
#if 0
ogs_pfcp_user_plane_report_t report;
#endif
uint32_t teid;
uint8_t qfi;
ogs_pfcp_pdr_t *pdr = NULL;
upf_sess_t *sess = NULL;
ogs_pfcp_subnet_t *subnet = NULL;
ogs_pfcp_dev_t *dev = NULL;
ogs_assert(fd != INVALID_SOCKET);
@ -150,25 +148,8 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data)
teid = be32toh(gtp_h->teid);
if (gtp_h->type == OGS_GTPU_MSGTYPE_END_MARKER) {
ogs_debug("[RECV] End Marker from [%s] : TEID[0x%x]",
OGS_ADDR(&from, buf), teid);
goto cleanup;
}
if (gtp_h->type == OGS_GTPU_MSGTYPE_ERR_IND) {
ogs_warn("[RECV] Error Indication from [%s]", OGS_ADDR(&from, buf));
goto cleanup;
}
if (gtp_h->type != OGS_GTPU_MSGTYPE_GPDU) {
ogs_error("[DROP] Invalid GTPU Type [%d]", gtp_h->type);
ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len);
goto cleanup;
}
ogs_debug("[RECV] GPU-U from [%s] : TEID[0x%x]",
OGS_ADDR(&from, buf), teid);
ogs_debug("[RECV] GPU-U Type [%d] from [%s] : TEID[0x%x]",
gtp_h->type, OGS_ADDR(&from, buf), teid);
qfi = 0;
if (gtp_h->flags & OGS_GTPU_FLAGS_E) {
@ -203,48 +184,65 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data)
}
ogs_assert(ogs_pkbuf_pull(pkbuf, len));
ip_h = (struct ip *)pkbuf->data;
ogs_assert(ip_h);
if (gtp_h->type == OGS_GTPU_MSGTYPE_END_MARKER) {
/* Nothing */
pdr = ogs_pfcp_pdr_find_by_teid_and_qfi(teid, qfi);
if (!pdr) {
#if 0 /* It's redundant log message */
ogs_warn("[DROP] Cannot find PDR : UPF-N3-TEID[0x%x] QFI[%d]",
teid, qfi);
#endif
goto cleanup;
}
ogs_assert(pdr->sess);
sess = UPF_SESS(pdr->sess);
ogs_assert(sess);
} else if (gtp_h->type == OGS_GTPU_MSGTYPE_ERR_IND) {
/* TODO */
if (ip_h->ip_v == 4 && sess->ipv4)
subnet = sess->ipv4->subnet;
else if (ip_h->ip_v == 6 && sess->ipv6)
subnet = sess->ipv6->subnet;
} else if (gtp_h->type == OGS_GTPU_MSGTYPE_GPDU) {
int rv;
if (!subnet) {
#if 0 /* It's redundant log message */
ogs_error("[DROP] Cannot find subnet V:%d, IPv4:%p, IPv6:%p",
ip_h->ip_v, sess->ipv4, sess->ipv6);
ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len);
#endif
goto cleanup;
}
struct ip *ip_h = NULL;
ogs_pfcp_pdr_t *pdr = NULL;
/* Check IPv6 */
if (ogs_app()->parameter.no_slaac == 0 && ip_h->ip_v == 6) {
rv = upf_gtp_handle_slaac(sess, pkbuf);
if (rv == UPF_GTP_HANDLED) {
upf_sess_t *sess = NULL;
ogs_pfcp_subnet_t *subnet = NULL;
ogs_pfcp_dev_t *dev = NULL;
ip_h = (struct ip *)pkbuf->data;
ogs_assert(ip_h);
pdr = ogs_pfcp_pdr_find_by_teid_and_qfi(teid, qfi);
if (!pdr) {
/* TODO : Send Error Indication */
goto cleanup;
}
ogs_assert(rv == OGS_OK);
}
ogs_assert(pdr->sess);
sess = UPF_SESS(pdr->sess);
ogs_assert(sess);
dev = subnet->dev;
ogs_assert(dev);
if (ogs_write(dev->fd, pkbuf->data, pkbuf->len) <= 0)
ogs_error("ogs_write() failed");
if (ip_h->ip_v == 4 && sess->ipv4)
subnet = sess->ipv4->subnet;
else if (ip_h->ip_v == 6 && sess->ipv6)
subnet = sess->ipv6->subnet;
if (!subnet) {
#if 0 /* It's redundant log message */
ogs_error("[DROP] Cannot find subnet V:%d, IPv4:%p, IPv6:%p",
ip_h->ip_v, sess->ipv4, sess->ipv6);
ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len);
#endif
goto cleanup;
}
/* Check IPv6 */
if (ogs_app()->parameter.no_slaac == 0 && ip_h->ip_v == 6) {
rv = upf_gtp_handle_slaac(sess, pkbuf);
if (rv == UPF_GTP_HANDLED) {
goto cleanup;
}
ogs_assert(rv == OGS_OK);
}
dev = subnet->dev;
ogs_assert(dev);
if (ogs_write(dev->fd, pkbuf->data, pkbuf->len) <= 0)
ogs_error("ogs_write() failed");
} else {
ogs_error("[DROP] Invalid GTPU Type [%d]", gtp_h->type);
ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len);
}
cleanup:
ogs_pkbuf_free(pkbuf);

View File

@ -48,7 +48,10 @@ static void setup_gtp_node(ogs_pfcp_far_t *far)
upf_self()->gtpu_sock, upf_self()->gtpu_sock6, gnode);
ogs_assert(rv == OGS_OK);
}
OGS_SETUP_GTP_NODE(far, gnode);
ogs_pfcp_far_hash_set(far);
}
void upf_n4_handle_session_establishment_request(

View File

@ -95,6 +95,7 @@ void test_5gc_init(void)
ogs_log_install_domain(&__ogs_ngap_domain, "ngap", OGS_LOG_ERROR);
ogs_log_install_domain(&__ogs_dbi_domain, "dbi", OGS_LOG_ERROR);
ogs_log_install_domain(&__ogs_nas_domain, "nas", OGS_LOG_ERROR);
ogs_log_install_domain(&__ogs_gtp_domain, "gtp", OGS_LOG_ERROR);
ogs_sctp_init(ogs_app()->usrsctp.udp_port);
ogs_assert(ogs_dbi_init(ogs_app()->db_uri) == OGS_OK);

View File

@ -119,6 +119,7 @@ void test_app_init(void)
ogs_log_install_domain(&__ogs_diam_domain, "diam", OGS_LOG_ERROR);
ogs_log_install_domain(&__ogs_dbi_domain, "dbi", OGS_LOG_ERROR);
ogs_log_install_domain(&__ogs_nas_domain, "nas", OGS_LOG_ERROR);
ogs_log_install_domain(&__ogs_gtp_domain, "gtp", OGS_LOG_ERROR);
ogs_sctp_init(ogs_app()->usrsctp.udp_port);
ogs_assert(ogs_dbi_init(ogs_app()->db_uri) == OGS_OK);

View File

@ -100,6 +100,7 @@ void test_epc_init(void)
ogs_log_install_domain(&__ogs_diam_domain, "diam", OGS_LOG_ERROR);
ogs_log_install_domain(&__ogs_dbi_domain, "dbi", OGS_LOG_ERROR);
ogs_log_install_domain(&__ogs_nas_domain, "nas", OGS_LOG_ERROR);
ogs_log_install_domain(&__ogs_gtp_domain, "gtp", OGS_LOG_ERROR);
ogs_sctp_init(ogs_app()->usrsctp.udp_port);
ogs_assert(ogs_dbi_init(ogs_app()->db_uri) == OGS_OK);

View File

@ -798,12 +798,458 @@ static void test2_func(abts_case *tc, void *data)
test_ue_remove(test_ue);
}
static void test3_func(abts_case *tc, void *data)
{
int rv;
ogs_socknode_t *s1ap;
ogs_socknode_t *gtpu;
ogs_pkbuf_t *emmbuf;
ogs_pkbuf_t *esmbuf;
ogs_pkbuf_t *sendbuf;
ogs_pkbuf_t *recvbuf;
ogs_s1ap_message_t message;
ogs_nas_5gs_mobile_identity_suci_t mobile_identity_suci;
test_ue_t *test_ue = NULL;
test_sess_t *sess = NULL;
test_bearer_t *bearer = NULL;
uint32_t enb_ue_s1ap_id;
uint64_t mme_ue_s1ap_id;
const char *_k_string = "465b5ce8b199b49faa5f0a2ee238a6bc";
uint8_t k[OGS_KEY_LEN];
const char *_opc_string = "e8ed289deba952e4283b54e88e6183ca";
uint8_t opc[OGS_KEY_LEN];
mongoc_collection_t *collection = NULL;
bson_t *doc = NULL;
int64_t count = 0;
bson_error_t error;
const char *json =
"{"
"\"_id\" : { \"$oid\" : \"310014158b8861d7605378c6\" }, "
"\"imsi\" : \"901707364000060\", "
"\"pdn\" : ["
"{"
"\"apn\" : \"internet\", "
"\"_id\" : { \"$oid\" : \"310014158b8861d7605378c7\" }, "
"\"ambr\" : {"
"\"uplink\" : { \"$numberLong\" : \"1000000\" }, "
"\"downlink\" : { \"$numberLong\" : \"1000000\" } "
"},"
"\"qos\" : { "
"\"qci\" : 9, "
"\"arp\" : { "
"\"priority_level\" : 15,"
"\"pre_emption_vulnerability\" : 1, "
"\"pre_emption_capability\" : 1"
"} "
"}, "
"\"type\" : 2"
"}"
"],"
"\"ambr\" : { "
"\"uplink\" : { \"$numberLong\" : \"1000000\" }, "
"\"downlink\" : { \"$numberLong\" : \"1000000\" } "
"},"
"\"subscribed_rau_tau_timer\" : 12,"
"\"network_access_mode\" : 2, "
"\"subscriber_status\" : 0, "
"\"access_restriction_data\" : 32, "
"\"security\" : { "
"\"k\" : \"465B5CE8 B199B49F AA5F0A2E E238A6BC\", "
"\"opc\" : \"E8ED289D EBA952E4 283B54E8 8E6183CA\", "
"\"amf\" : \"8000\", "
"\"sqn\" : { \"$numberLong\" : \"64\" } "
"}, "
"\"__v\" : 0 "
"}";
/* Setup Test UE & Session Context */
memset(&mobile_identity_suci, 0, sizeof(mobile_identity_suci));
mobile_identity_suci.h.supi_format = OGS_NAS_5GS_SUPI_FORMAT_IMSI;
mobile_identity_suci.h.type = OGS_NAS_5GS_MOBILE_IDENTITY_SUCI;
mobile_identity_suci.routing_indicator1 = 0;
mobile_identity_suci.routing_indicator2 = 0xf;
mobile_identity_suci.routing_indicator3 = 0xf;
mobile_identity_suci.routing_indicator4 = 0xf;
mobile_identity_suci.protection_scheme_id = OGS_NAS_5GS_NULL_SCHEME;
mobile_identity_suci.home_network_pki_value = 0;
mobile_identity_suci.scheme_output[0] = 0x37;
mobile_identity_suci.scheme_output[1] = 0x46;
mobile_identity_suci.scheme_output[2] = 0;
mobile_identity_suci.scheme_output[3] = 0;
mobile_identity_suci.scheme_output[4] = 0x06;
test_ue = test_ue_add_by_suci(&mobile_identity_suci, 13);
ogs_assert(test_ue);
test_ue->e_cgi.cell_id = 0x54f6401;
test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE;
test_ue->nas.value = OGS_NAS_ATTACH_TYPE_EPS_ATTACH;
OGS_HEX(_k_string, strlen(_k_string), test_ue->k);
OGS_HEX(_opc_string, strlen(_opc_string), test_ue->opc);
sess = test_sess_add_by_apn(test_ue, "internet");
ogs_assert(sess);
/* eNB connects to MME */
s1ap = tests1ap_client(AF_INET);
ABTS_PTR_NOTNULL(tc, s1ap);
/* eNB connects to SGW */
gtpu = test_gtpu_server(1, AF_INET);
ABTS_PTR_NOTNULL(tc, gtpu);
/* Send S1-Setup Reqeust */
sendbuf = test_s1ap_build_s1_setup_request(
S1AP_ENB_ID_PR_macroENB_ID, 0x54f64);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive S1-Setup Response */
recvbuf = testenb_s1ap_read(s1ap);
ABTS_PTR_NOTNULL(tc, recvbuf);
tests1ap_recv(NULL, recvbuf);
/********** Insert Subscriber in Database */
collection = mongoc_client_get_collection(
ogs_mongoc()->client, ogs_mongoc()->name, "subscribers");
ABTS_PTR_NOTNULL(tc, collection);
doc = BCON_NEW("imsi", BCON_UTF8(test_ue->imsi));
ABTS_PTR_NOTNULL(tc, doc);
count = mongoc_collection_count (
collection, MONGOC_QUERY_NONE, doc, 0, 0, NULL, &error);
if (count) {
ABTS_TRUE(tc, mongoc_collection_remove(collection,
MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error))
}
bson_destroy(doc);
doc = bson_new_from_json((const uint8_t *)json, -1, &error);;
ABTS_PTR_NOTNULL(tc, doc);
ABTS_TRUE(tc, mongoc_collection_insert(collection,
MONGOC_INSERT_NONE, doc, NULL, &error));
bson_destroy(doc);
doc = BCON_NEW("imsi", BCON_UTF8(test_ue->imsi));
ABTS_PTR_NOTNULL(tc, doc);
do {
count = mongoc_collection_count (
collection, MONGOC_QUERY_NONE, doc, 0, 0, NULL, &error);
} while (count == 0);
bson_destroy(doc);
collection = mongoc_client_get_collection(
ogs_mongoc()->client, ogs_mongoc()->name, "subscribers");
ABTS_PTR_NOTNULL(tc, collection);
/* Send Attach Request */
memset(&sess->pdn_connectivity_param,
0, sizeof(sess->pdn_connectivity_param));
sess->pdn_connectivity_param.eit = 1;
sess->pdn_connectivity_param.pco = 1;
esmbuf = testesm_build_pdn_connectivity_request(sess);
ABTS_PTR_NOTNULL(tc, esmbuf);
memset(&test_ue->attach_request_param,
0, sizeof(test_ue->attach_request_param));
test_ue->attach_request_param.ms_network_feature_support = 1;
emmbuf = testemm_build_attach_request(test_ue, esmbuf);
ABTS_PTR_NOTNULL(tc, emmbuf);
memset(&test_ue->initial_ue_param, 0, sizeof(test_ue->initial_ue_param));
sendbuf = test_s1ap_build_initial_ue_message(
test_ue, emmbuf, S1AP_RRC_Establishment_Cause_mo_Signalling, false);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive Authentication Request */
recvbuf = testenb_s1ap_read(s1ap);
ABTS_PTR_NOTNULL(tc, recvbuf);
tests1ap_recv(test_ue, recvbuf);
/* Send Authentication response */
emmbuf = testemm_build_authentication_response(test_ue);
ABTS_PTR_NOTNULL(tc, emmbuf);
sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, emmbuf);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive Security mode Command */
recvbuf = testenb_s1ap_read(s1ap);
ABTS_PTR_NOTNULL(tc, recvbuf);
tests1ap_recv(test_ue, recvbuf);
/* Send Security mode complete */
test_ue->mobile_identity_imeisv_presence = true;
emmbuf = testemm_build_security_mode_complete(test_ue);
ABTS_PTR_NOTNULL(tc, emmbuf);
sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, emmbuf);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive ESM Information Request */
recvbuf = testenb_s1ap_read(s1ap);
ABTS_PTR_NOTNULL(tc, recvbuf);
tests1ap_recv(test_ue, recvbuf);
/* Send ESM Information Response */
esmbuf = testesm_build_esm_information_response(sess);
ABTS_PTR_NOTNULL(tc, esmbuf);
sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, esmbuf);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive Initial Context Setup Request +
* Attach Accept +
* Activate Default Bearer Context Request */
recvbuf = testenb_s1ap_read(s1ap);
ABTS_PTR_NOTNULL(tc, recvbuf);
tests1ap_recv(test_ue, recvbuf);
/* Send UE Capability Info Indication */
sendbuf = tests1ap_build_ue_radio_capability_info_indication(test_ue);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Send Initial Context Setup Response */
sendbuf = test_s1ap_build_initial_context_setup_response(test_ue);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Send Attach Complete + Activate default EPS bearer cotext accept */
test_ue->nr_cgi.cell_id = 0x1234502;
bearer = test_bearer_find_by_ue_ebi(test_ue, 5);
ogs_assert(bearer);
esmbuf = testesm_build_activate_default_eps_bearer_context_accept(
bearer, false);
ABTS_PTR_NOTNULL(tc, esmbuf);
emmbuf = testemm_build_attach_complete(test_ue, esmbuf);
ABTS_PTR_NOTNULL(tc, emmbuf);
sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, emmbuf);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive EMM information */
recvbuf = testenb_s1ap_read(s1ap);
ABTS_PTR_NOTNULL(tc, recvbuf);
tests1ap_recv(test_ue, recvbuf);
/* Send UE Context Release Request */
sendbuf = test_s1ap_build_ue_context_release_request(test_ue,
S1AP_Cause_PR_radioNetwork, S1AP_CauseRadioNetwork_user_inactivity);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive UE Context Release Command */
recvbuf = testenb_s1ap_read(s1ap);
ABTS_PTR_NOTNULL(tc, recvbuf);
tests1ap_recv(test_ue, recvbuf);
/* Send UE Context Release Complete */
sendbuf = test_s1ap_build_ue_context_release_complete(test_ue);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Send GTP-U ICMP Packet */
rv = test_gtpu_send_ping(gtpu, bearer, TEST_PING_IPV4);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive S1-Paging */
recvbuf = testenb_s1ap_read(s1ap);
ABTS_PTR_NOTNULL(tc, recvbuf);
tests1ap_recv(test_ue, recvbuf);
/* Send Service Request */
emmbuf = testemm_build_service_request(test_ue);
ABTS_PTR_NOTNULL(tc, emmbuf);
sendbuf = test_s1ap_build_initial_ue_message(
test_ue, emmbuf, S1AP_RRC_Establishment_Cause_mo_Data, true);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive Initial Context Setup Request */
recvbuf = testenb_s1ap_read(s1ap);
ABTS_PTR_NOTNULL(tc, recvbuf);
tests1ap_recv(test_ue, recvbuf);
/* Send Initial Context Setup Response */
sendbuf = test_s1ap_build_initial_context_setup_response(test_ue);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive GTP-U ICMP Packet */
recvbuf = test_gtpu_read(gtpu);
ABTS_PTR_NOTNULL(tc, recvbuf);
ogs_pkbuf_free(recvbuf);
/* Send GTP-U ICMP Packet */
rv = test_gtpu_send_ping(gtpu, bearer, TEST_PING_IPV4);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive GTP-U ICMP Packet */
recvbuf = test_gtpu_read(gtpu);
ABTS_PTR_NOTNULL(tc, recvbuf);
ogs_pkbuf_free(recvbuf);
/* Send Error Indication */
rv = test_gtpu_send_error_indication(gtpu, bearer);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive UE Context Release Command */
recvbuf = testenb_s1ap_read(s1ap);
ABTS_PTR_NOTNULL(tc, recvbuf);
tests1ap_recv(test_ue, recvbuf);
/* Send UE Context Release Complete */
sendbuf = test_s1ap_build_ue_context_release_complete(test_ue);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive S1-Paging */
recvbuf = testenb_s1ap_read(s1ap);
ABTS_PTR_NOTNULL(tc, recvbuf);
tests1ap_recv(test_ue, recvbuf);
/* Send Service Request */
emmbuf = testemm_build_service_request(test_ue);
ABTS_PTR_NOTNULL(tc, emmbuf);
sendbuf = test_s1ap_build_initial_ue_message(
test_ue, emmbuf, S1AP_RRC_Establishment_Cause_mo_Data, true);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive Initial Context Setup Request */
recvbuf = testenb_s1ap_read(s1ap);
ABTS_PTR_NOTNULL(tc, recvbuf);
tests1ap_recv(test_ue, recvbuf);
/* Send Initial Context Setup Response */
sendbuf = test_s1ap_build_initial_context_setup_response(test_ue);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Send UE Context Release Request */
sendbuf = test_s1ap_build_ue_context_release_request(test_ue,
S1AP_Cause_PR_radioNetwork, S1AP_CauseRadioNetwork_user_inactivity);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive UE Context Release Command */
recvbuf = testenb_s1ap_read(s1ap);
ABTS_PTR_NOTNULL(tc, recvbuf);
tests1ap_recv(test_ue, recvbuf);
/* Send UE Context Release Complete */
sendbuf = test_s1ap_build_ue_context_release_complete(test_ue);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Send Error Indication */
rv = test_gtpu_send_error_indication(gtpu, bearer);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive S1-Paging */
recvbuf = testenb_s1ap_read(s1ap);
ABTS_PTR_NOTNULL(tc, recvbuf);
tests1ap_recv(test_ue, recvbuf);
/* Send Service Request */
emmbuf = testemm_build_service_request(test_ue);
ABTS_PTR_NOTNULL(tc, emmbuf);
sendbuf = test_s1ap_build_initial_ue_message(
test_ue, emmbuf, S1AP_RRC_Establishment_Cause_mo_Data, true);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive Initial Context Setup Request */
recvbuf = testenb_s1ap_read(s1ap);
ABTS_PTR_NOTNULL(tc, recvbuf);
tests1ap_recv(test_ue, recvbuf);
/* Send Initial Context Setup Response */
sendbuf = test_s1ap_build_initial_context_setup_response(test_ue);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Send GTP-U ICMP Packet */
rv = test_gtpu_send_ping(gtpu, bearer, TEST_PING_IPV4);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive GTP-U ICMP Packet */
recvbuf = test_gtpu_read(gtpu);
ABTS_PTR_NOTNULL(tc, recvbuf);
ogs_pkbuf_free(recvbuf);
/* Send UE Context Release Request */
sendbuf = test_s1ap_build_ue_context_release_request(test_ue,
S1AP_Cause_PR_radioNetwork, S1AP_CauseRadioNetwork_user_inactivity);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive UE Context Release Command */
recvbuf = testenb_s1ap_read(s1ap);
ABTS_PTR_NOTNULL(tc, recvbuf);
tests1ap_recv(test_ue, recvbuf);
/* Send UE Context Release Complete */
sendbuf = test_s1ap_build_ue_context_release_complete(test_ue);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
ogs_msleep(300);
/********** Remove Subscriber in Database */
doc = BCON_NEW("imsi", BCON_UTF8(test_ue->imsi));
ABTS_PTR_NOTNULL(tc, doc);
ABTS_TRUE(tc, mongoc_collection_remove(collection,
MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error))
bson_destroy(doc);
mongoc_collection_destroy(collection);
/* eNB disonncect from MME */
testenb_s1ap_close(s1ap);
/* eNB disonncect from SGW */
test_gtpu_close(gtpu);
test_ue_remove(test_ue);
}
abts_suite *test_idle(abts_suite *suite)
{
suite = ADD_SUITE(suite)
abts_run_test(suite, test1_func, NULL);
abts_run_test(suite, test2_func, NULL);
abts_run_test(suite, test3_func, NULL);
return suite;
}

View File

@ -97,18 +97,62 @@ void test_gtpu_close(ogs_socknode_t *node)
#include <netinet/icmp6.h>
#endif
int test_gtpu_send(
ogs_socknode_t *node, test_bearer_t *bearer,
ogs_gtp_header_t *gtp_hdesc, ogs_gtp_extension_header_t *ext_hdesc,
ogs_pkbuf_t *pkbuf)
{
ogs_gtp_node_t gnode;
test_sess_t *sess = NULL;
ogs_assert(gtp_hdesc);
ogs_assert(ext_hdesc);
ogs_assert(pkbuf);
ogs_assert(bearer);
sess = bearer->sess;
ogs_assert(sess);
memset(&gnode, 0, sizeof(ogs_gtp_node_t));
gnode.addr.ogs_sin_port = htobe16(OGS_GTPV1_U_UDP_PORT);
gnode.sock = node->sock;
if (bearer->qfi) {
if (sess->upf_n3_ip.ipv4) {
gnode.addr.ogs_sa_family = AF_INET;
gnode.addr.sin.sin_addr.s_addr = sess->upf_n3_ip.addr;
} else {
ogs_fatal("Not implemented");
ogs_assert_if_reached();
}
} else if (bearer->ebi) {
if (bearer->sgw_s1u_ip.ipv4) {
gnode.addr.ogs_sa_family = AF_INET;
gnode.addr.sin.sin_addr.s_addr = bearer->sgw_s1u_ip.addr;
} else {
ogs_fatal("Not implemented");
ogs_assert_if_reached();
}
} else {
ogs_fatal("No QFI[%d] and EBI[%d]", bearer->qfi, bearer->ebi);
ogs_assert_if_reached();
}
return ogs_gtp_send_user_plane(&gnode, gtp_hdesc, ext_hdesc, pkbuf);
}
int test_gtpu_send_ping(
ogs_socknode_t *node, test_bearer_t *bearer, const char *dst_ip)
{
int rv;
ssize_t sent;
test_sess_t *sess = NULL;
ogs_sockaddr_t upf;
ogs_gtp_header_t gtp_hdesc;
ogs_gtp_extension_header_t ext_hdesc;
ogs_pkbuf_t *pkbuf = NULL;
ogs_gtp_header_t *gtp_h = NULL;
ogs_gtp_extension_header_t *ext_h = NULL;
ogs_ipsubnet_t dst_ipsub;
ogs_assert(bearer);
@ -119,79 +163,21 @@ int test_gtpu_send_ping(
rv = ogs_ipsubnet(&dst_ipsub, dst_ip, NULL);
ogs_assert(rv == OGS_OK);
memset(&upf, 0, sizeof(ogs_sockaddr_t));
upf.ogs_sin_port = htobe16(OGS_GTPV1_U_UDP_PORT);
pkbuf = ogs_pkbuf_alloc(
NULL, 200 /* enough for ICMP; use smaller buffer */);
ogs_assert(pkbuf);
ogs_pkbuf_put(pkbuf, 200);
ogs_pkbuf_reserve(pkbuf, OGS_GTPV1U_5GC_HEADER_LEN);
ogs_pkbuf_put(pkbuf, 200-OGS_GTPV1U_5GC_HEADER_LEN);
memset(pkbuf->data, 0, pkbuf->len);
gtp_h = (ogs_gtp_header_t *)pkbuf->data;
if (bearer->qfi) {
/* 5G Core */
gtp_h->flags = 0x34;
gtp_h->teid = htobe32(sess->upf_n3_teid);
if (sess->upf_n3_ip.ipv4) {
upf.ogs_sa_family = AF_INET;
upf.sin.sin_addr.s_addr = sess->upf_n3_ip.addr;
} else {
ogs_fatal("Not implemented");
ogs_assert_if_reached();
}
} else if (bearer->ebi) {
/* EPC */
gtp_h->flags = 0x30;
gtp_h->teid = htobe32(bearer->sgw_s1u_teid);
if (bearer->sgw_s1u_ip.ipv4) {
upf.ogs_sa_family = AF_INET;
upf.sin.sin_addr.s_addr = bearer->sgw_s1u_ip.addr;
} else {
ogs_fatal("Not implemented");
ogs_assert_if_reached();
}
} else {
ogs_fatal("No QFI[%d] and EBI[%d]", bearer->qfi, bearer->ebi);
ogs_assert_if_reached();
}
gtp_h->type = OGS_GTPU_MSGTYPE_GPDU;
if (bearer->qfi) {
ext_h = (ogs_gtp_extension_header_t *)
(pkbuf->data + OGS_GTPV1U_HEADER_LEN);
ext_h->type = OGS_GTP_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER;
ext_h->len = 1;
ext_h->pdu_type =
OGS_GTP_EXTENSION_HEADER_PDU_TYPE_UL_PDU_SESSION_INFORMATION;
ext_h->qos_flow_identifier = bearer->qfi;
ext_h->next_type =
OGS_GTP_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS;
}
if (dst_ipsub.family == AF_INET) {
struct ip *ip_h = NULL;
struct icmp *icmp_h = NULL;
if (bearer->qfi) {
gtp_h->length = htobe16(
sizeof *ip_h + ICMP_MINLEN +
OGS_GTPV1U_EXTENSION_HEADER_LEN + ext_h->len * 4);
ogs_pkbuf_trim(pkbuf, sizeof *ip_h + ICMP_MINLEN);
ip_h = (struct ip *)(pkbuf->data +
OGS_GTPV1U_HEADER_LEN +
OGS_GTPV1U_EXTENSION_HEADER_LEN + ext_h->len * 4);
icmp_h = (struct icmp *)((uint8_t *)ip_h + sizeof *ip_h);
} else {
gtp_h->length = htobe16(sizeof *ip_h + ICMP_MINLEN);
ip_h = (struct ip *)(pkbuf->data + OGS_GTPV1U_HEADER_LEN);
icmp_h = (struct icmp *)((uint8_t *)ip_h + sizeof *ip_h);
}
ip_h = (struct ip *)pkbuf->data;
icmp_h = (struct icmp *)((uint8_t *)ip_h + sizeof *ip_h);
ip_h->ip_v = 4;
ip_h->ip_hl = 5;
@ -217,15 +203,9 @@ int test_gtpu_send_ping(
uint8_t nxt = 0;
uint8_t *p = NULL;
if (bearer->qfi) {
gtp_h->length = htobe16(sizeof *ip6_h + sizeof *icmp6_h) +
OGS_GTPV1U_EXTENSION_HEADER_LEN + ext_h->len * 4;
p = (uint8_t *)pkbuf->data + OGS_GTPV1U_HEADER_LEN +
OGS_GTPV1U_EXTENSION_HEADER_LEN + ext_h->len * 4;
} else {
gtp_h->length = htobe16(sizeof *ip6_h + sizeof *icmp6_h);
p = (uint8_t *)pkbuf->data + OGS_GTPV1U_HEADER_LEN;
}
ogs_pkbuf_trim(pkbuf, sizeof *ip6_h + sizeof *icmp6_h);
p = (uint8_t *)pkbuf->data;
plen = htobe16(sizeof *icmp6_h);
nxt = IPPROTO_ICMPV6;
@ -246,7 +226,7 @@ int test_gtpu_send_ping(
icmp6_h->icmp6_cksum = ogs_in_cksum(
(uint16_t *)ip6_h, sizeof *ip6_h + sizeof *icmp6_h);
ip6_h->ip6_flow = htonl(0x60000001);
ip6_h->ip6_flow = htobe32(0x60000001);
ip6_h->ip6_plen = plen;
ip6_h->ip6_nxt = nxt;;
ip6_h->ip6_hlim = 0xff;
@ -258,117 +238,117 @@ int test_gtpu_send_ping(
ogs_assert_if_reached();
}
ogs_assert(node);
ogs_assert(node->sock);
memset(&gtp_hdesc, 0, sizeof(gtp_hdesc));
memset(&ext_hdesc, 0, sizeof(ext_hdesc));
sent = ogs_sendto(node->sock->fd, pkbuf->data, pkbuf->len, 0, &upf);
ogs_pkbuf_free(pkbuf);
if (sent < 0 || sent != pkbuf->len)
return OGS_ERROR;
gtp_hdesc.type = OGS_GTPU_MSGTYPE_GPDU;
return OGS_OK;
}
int test_gtpu_send_slacc_rs(ogs_socknode_t *node, test_bearer_t *bearer)
{
int rv;
ssize_t sent;
test_sess_t *sess = NULL;
ogs_sockaddr_t upf;
ogs_pkbuf_t *pkbuf = NULL;
ogs_gtp_header_t *gtp_h = NULL;
ogs_gtp_extension_header_t *ext_h = NULL;
unsigned char *ip_h = NULL;
const char *payload =
"6000000000083aff fe80000000000000 0000000000000002"
"ff02000000000000 0000000000000002 85007d3500000000";
unsigned char tmp[OGS_MAX_SDU_LEN];
ogs_assert(bearer);
sess = bearer->sess;
ogs_assert(sess);
memset(&upf, 0, sizeof(ogs_sockaddr_t));
upf.ogs_sin_port = htobe16(OGS_GTPV1_U_UDP_PORT);
pkbuf = ogs_pkbuf_alloc(
NULL, 200 /* enough for ICMP; use smaller buffer */);
ogs_assert(pkbuf);
ogs_pkbuf_put(pkbuf, 200);
memset(pkbuf->data, 0, pkbuf->len);
gtp_h = (ogs_gtp_header_t *)pkbuf->data;
if (bearer->qfi) {
/* 5G Core */
gtp_h->flags = 0x36;
gtp_h->teid = htobe32(sess->upf_n3_teid);
if (sess->upf_n3_ip.ipv4) {
upf.ogs_sa_family = AF_INET;
upf.sin.sin_addr.s_addr = sess->upf_n3_ip.addr;
} else {
ogs_fatal("Not implemented");
ogs_assert_if_reached();
}
gtp_hdesc.teid = sess->upf_n3_teid;
ext_hdesc.qos_flow_identifier = bearer->qfi;
} else if (bearer->ebi) {
/* EPC */
gtp_h->flags = 0x32;
gtp_h->teid = htobe32(bearer->sgw_s1u_teid);
gtp_hdesc.teid = bearer->sgw_s1u_teid;
if (bearer->sgw_s1u_ip.ipv4) {
upf.ogs_sa_family = AF_INET;
upf.sin.sin_addr.s_addr = bearer->sgw_s1u_ip.addr;
} else {
ogs_fatal("Not implemented");
ogs_assert_if_reached();
}
} else {
ogs_fatal("No QFI[%d] and EBI[%d]", bearer->qfi, bearer->ebi);
ogs_assert_if_reached();
}
gtp_h->type = OGS_GTPU_MSGTYPE_GPDU;
return test_gtpu_send(node, bearer, &gtp_hdesc, &ext_hdesc, pkbuf);
}
if (bearer->qfi) {
ext_h = (ogs_gtp_extension_header_t *)
(pkbuf->data + OGS_GTPV1U_HEADER_LEN);
ext_h->type = OGS_GTP_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER;
ext_h->len = 1;
ext_h->pdu_type =
OGS_GTP_EXTENSION_HEADER_PDU_TYPE_UL_PDU_SESSION_INFORMATION;
ext_h->qos_flow_identifier = bearer->qfi;
ext_h->next_type =
OGS_GTP_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS;
}
int test_gtpu_send_slacc_rs(ogs_socknode_t *node, test_bearer_t *bearer)
{
test_sess_t *sess = NULL;
if (bearer->qfi) {
gtp_h->length = htobe16(52 +
OGS_GTPV1U_EXTENSION_HEADER_LEN + ext_h->len * 4);
ogs_gtp_header_t gtp_hdesc;
ogs_gtp_extension_header_t ext_hdesc;
ip_h = (pkbuf->data +
OGS_GTPV1U_HEADER_LEN + 4 +
OGS_GTPV1U_EXTENSION_HEADER_LEN + ext_h->len * 4);
} else {
gtp_h->length = htobe16(52);
ogs_pkbuf_t *pkbuf = NULL;
ip_h = (pkbuf->data + OGS_GTPV1U_HEADER_LEN + 4);
}
const char *payload =
"6000000000083aff fe80000000000000 0000000000000002"
"ff02000000000000 0000000000000002 85007d3500000000";
unsigned char tmp[OGS_MAX_SDU_LEN];
int payload_len = 48;
ogs_assert(node);
ogs_assert(node->sock);
ogs_assert(bearer);
sess = bearer->sess;
ogs_assert(sess);
pkbuf = ogs_pkbuf_alloc(
NULL, 200 /* enough for ICMP; use smaller buffer */);
ogs_assert(pkbuf);
ogs_pkbuf_reserve(pkbuf, OGS_GTPV1U_5GC_HEADER_LEN);
ogs_pkbuf_put(pkbuf, 200-OGS_GTPV1U_5GC_HEADER_LEN);
memset(pkbuf->data, 0, pkbuf->len);
OGS_HEX(payload, strlen(payload), tmp);
memcpy(ip_h, tmp, 48);
memcpy(pkbuf->data, tmp, payload_len);
sent = ogs_sendto(node->sock->fd, pkbuf->data, pkbuf->len, 0, &upf);
ogs_pkbuf_free(pkbuf);
if (sent < 0 || sent != pkbuf->len)
return OGS_ERROR;
ogs_pkbuf_trim(pkbuf, payload_len);
return OGS_OK;
memset(&gtp_hdesc, 0, sizeof(gtp_hdesc));
memset(&ext_hdesc, 0, sizeof(ext_hdesc));
gtp_hdesc.type = OGS_GTPU_MSGTYPE_GPDU;
gtp_hdesc.flags = OGS_GTPU_FLAGS_S;
if (bearer->qfi) {
gtp_hdesc.teid = sess->upf_n3_teid;
ext_hdesc.qos_flow_identifier = bearer->qfi;
} else if (bearer->ebi) {
gtp_hdesc.teid = bearer->sgw_s1u_teid;
} else {
ogs_fatal("No QFI[%d] and EBI[%d]", bearer->qfi, bearer->ebi);
ogs_assert_if_reached();
}
return test_gtpu_send(node, bearer, &gtp_hdesc, &ext_hdesc, pkbuf);
}
int test_gtpu_send_error_indication(
ogs_socknode_t *node, test_bearer_t *bearer)
{
test_sess_t *sess = NULL;
uint32_t teid = 0;
ogs_gtp_header_t gtp_hdesc;
ogs_gtp_extension_header_t ext_hdesc;
ogs_pkbuf_t *pkbuf = NULL;
ogs_assert(bearer);
sess = bearer->sess;
ogs_assert(sess);
ogs_assert(bearer);
if (bearer->qfi) {
/* 5GC */
teid = sess->gnb_n3_teid;
} else if (bearer->ebi) {
/* EPC */
teid = bearer->enb_s1u_teid;
} else {
ogs_fatal("No QFI[%d] and EBI[%d]", bearer->qfi, bearer->ebi);
ogs_assert_if_reached();
}
pkbuf = ogs_gtp_build_error_indication(teid, node->addr);
ogs_assert(pkbuf);
memset(&gtp_hdesc, 0, sizeof(gtp_hdesc));
memset(&ext_hdesc, 0, sizeof(ext_hdesc));
gtp_hdesc.type = OGS_GTPU_MSGTYPE_ERR_IND;
gtp_hdesc.flags = OGS_GTPU_FLAGS_S|OGS_GTPU_FLAGS_E;
ext_hdesc.type = OGS_GTP_EXTENSION_HEADER_TYPE_UDP_PORT;
return test_gtpu_send(node, bearer, &gtp_hdesc, &ext_hdesc, pkbuf);
}

View File

@ -31,9 +31,15 @@ ogs_socknode_t *test_gtpu_server(int index, int family);
ogs_pkbuf_t *test_gtpu_read(ogs_socknode_t *node);
void test_gtpu_close(ogs_socknode_t *node);
int test_gtpu_send(
ogs_socknode_t *node, test_bearer_t *bearer,
ogs_gtp_header_t *gtp_hdesc, ogs_gtp_extension_header_t *ext_hdesc,
ogs_pkbuf_t *pkbuf);
int test_gtpu_send_ping(
ogs_socknode_t *node, test_bearer_t *bearer, const char *dst_ip);
int test_gtpu_send_slacc_rs(ogs_socknode_t *node, test_bearer_t *bearer);
int test_gtpu_send_error_indication(
ogs_socknode_t *node, test_bearer_t *bearer);
#ifdef __cplusplus
}