Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
pespin committed Dec 13, 2023
1 parent bd0be1d commit 4aba7dc
Show file tree
Hide file tree
Showing 10 changed files with 453 additions and 1 deletion.
194 changes: 194 additions & 0 deletions lib/gtp/v1/types.c
Original file line number Diff line number Diff line change
Expand Up @@ -398,3 +398,197 @@ int16_t ogs_gtp1_build_qos_profile(ogs_tlv_octet_t *octet,
octet->len = 6;
return octet->len;
}

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

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_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);

CHECK_SPACE_ERR(1);
*ptr++ = decoded->qos_sub_len;
CHECK_SPACE_ERR(decoded->qos_sub_len);
memcpy(ptr, &decoded->qos_sub[0], decoded->qos_sub_len);
ptr += decoded->qos_sub_len;

CHECK_SPACE_ERR(1);
*ptr++ = decoded->qos_req_len;
CHECK_SPACE_ERR(decoded->qos_req_len);
memcpy(ptr, &decoded->qos_req[0], decoded->qos_req_len);
ptr += decoded->qos_req_len;

CHECK_SPACE_ERR(1);
*ptr++ = decoded->qos_neg_len;
CHECK_SPACE_ERR(decoded->qos_neg_len);
memcpy(ptr, &decoded->qos_neg[0], decoded->qos_neg_len);
ptr += decoded->qos_neg_len;

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++ = 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.ogs_sa_family == AF_INET6 ?
OGS_GTP_GSN_ADDRESS_IPV6_LEN : OGS_GTP_GSN_ADDRESS_IPV4_LEN;
CHECK_SPACE_ERR(1 + (*ptr));
memcpy(ptr + 1,
decoded->ggsn_address_c.ogs_sa_family == AF_INET6 ?
(uint8_t *)decoded->ggsn_address_c.sin6.sin6_addr.s6_addr :
(uint8_t *)&decoded->ggsn_address_c.sin.sin_addr.s_addr,
*ptr);
ptr += 1 + *ptr;

/* GGSN Address for User Traffic Length */
CHECK_SPACE_ERR(1);
*ptr = decoded->ggsn_address_u.ogs_sa_family == AF_INET6 ?
OGS_GTP_GSN_ADDRESS_IPV6_LEN : OGS_GTP_GSN_ADDRESS_IPV4_LEN;
CHECK_SPACE_ERR(1 + (*ptr));
memcpy(ptr + 1,
decoded->ggsn_address_u.ogs_sa_family == AF_INET6 ?
(uint8_t *)decoded->ggsn_address_u.sin6.sin6_addr.s6_addr :
(uint8_t *)&decoded->ggsn_address_u.sin.sin_addr.s_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) {
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 octet->len;
#undef CHECK_SPACE_ERR
}
48 changes: 48 additions & 0 deletions lib/gtp/v1/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -300,4 +300,52 @@ 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.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

/* Not explicitly defined in a table: */
#define OGS_GTP1_PDPCTX_REORDERING_REQUIRED_NO 0
#define OGS_GTP1_PDPCTX_REORDERING_REQUIRED_YES 1

typedef struct ogs_gtp1_pdp_context_decoded_s {
uint8_t ea:1; /* OGS_GTP1_PDPCTX_REORDERING_REQUIRED_* */
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;
uint8_t qos_sub[OGS_GTP1_QOS_PROFILE_MAX_LEN];
uint8_t qos_sub_len;
uint8_t qos_req[OGS_GTP1_QOS_PROFILE_MAX_LEN];
uint8_t qos_req_len;
uint8_t qos_neg[OGS_GTP1_QOS_PROFILE_MAX_LEN];
uint8_t qos_neg_len;
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_sockaddr_t ggsn_address_c;
ogs_sockaddr_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_tlv_octet_t *octet,
const ogs_gtp1_pdp_context_decoded_t *decoded, uint8_t *data, int data_len);

#endif /* OGS_GTP1_TYPES_H */
5 changes: 5 additions & 0 deletions src/mme/mme-context.h
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,11 @@ struct mme_ue_s {
char a_msisdn_bcd[OGS_MAX_MSISDN_BCD_LEN+1];

mme_p_tmsi_t p_tmsi;
struct {
uint32_t sgsn_gn_teid;
ogs_ip_t sgsn_gn_ip;
ogs_ip_t sgsn_gn_ip_alt;
} gn;

struct {
mme_m_tmsi_t *m_tmsi;
Expand Down
71 changes: 70 additions & 1 deletion src/mme/mme-gn-build.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,75 @@

#include "mme-gn-build.h"

/* 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_debug("[Gn] build SGSN Context Response");


rsp = &gtp1_message.sgsn_context_response;
memset(&gtp1_message, 0, sizeof(ogs_gtp1_message_t));
gtp1_message.h.type = OGS_GTP1_SGSN_CONTEXT_RESPONSE_TYPE;

/* 3GPP TS 29.060 7.7.1 Cause, Mandatory */
rsp->cause.presence = 1;
rsp->cause.u8 = cause;

/* 3GPP TS 29.060 7.7.2 IMSI, Conditional */
rsp->imsi.presence = !!mme_ue;
if (rsp->imsi.presence) {
rsp->imsi.data = mme_ue->imsi;
rsp->imsi.len = mme_ue->imsi_len;
}

if (cause != OGS_GTP1_CAUSE_REQUEST_ACCEPTED)
goto build_ret;

ogs_assert(mme_ue);

/* FIXME: Reuse S11 TEID as local Gn interface for now */
rsp->tunnel_endpoint_identifier_control_plane.presence = 1;
rsp->tunnel_endpoint_identifier_control_plane.u32 = mme_ue->mme_s11_teid;

ogs_list_for_each(&mme_ue->sess_list, sess) {
if (!MME_HAVE_SGW_S1U_PATH(sess))
continue;
/* TODO: fill some structure with pdp contexts */
}

/* 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(NULL, ogs_gtp_self()->gtpc_addr,
&mme_gnc_gsnaddr, &gsn_len);
if (rv != OGS_OK) {
ogs_error("ogs_gtp1_sockaddr_to_gsn_addr() failed");
return NULL;
}
}
rsp->sgsn_address_for_control_plane.presence = 1;
rsp->sgsn_address_for_control_plane.data = &mme_gnc_gsnaddr;
rsp->sgsn_address_for_control_plane.len = gsn_len;

build_ret:
return ogs_gtp1_build_msg(&gtp1_message);
}

/* 3GPP TS 29.060 7.5.14.1 RAN Information Relay */
ogs_pkbuf_t *mme_gn_build_ran_information_relay(
uint8_t type, const uint8_t *buf, size_t len,
Expand Down Expand Up @@ -59,4 +128,4 @@ ogs_pkbuf_t *mme_gn_build_ran_information_relay(

gtp1_message.h.type = type;
return ogs_gtp1_build_msg(&gtp1_message);
}
}
3 changes: 3 additions & 0 deletions src/mme/mme-gn-build.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading

0 comments on commit 4aba7dc

Please sign in to comment.