This is done for better merging branch/LG_PRE_RELEASE_0.3 into trunk (same process is applied on this branch) git-svn-id: http://svn.eurecom.fr/openair4G/trunk@7073 818b1a75-f10b-46b9-bf7c-635c3b92a50f
1165 lines
29 KiB
C
1165 lines
29 KiB
C
/*******************************************************************************
|
|
OpenAirInterface
|
|
Copyright(c) 1999 - 2014 Eurecom
|
|
|
|
OpenAirInterface is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
|
|
OpenAirInterface is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with OpenAirInterface.The full GNU General Public License is
|
|
included in this distribution in the file called "COPYING". If not,
|
|
see <http://www.gnu.org/licenses/>.
|
|
|
|
Contact Information
|
|
OpenAirInterface Admin: openair_admin@eurecom.fr
|
|
OpenAirInterface Tech : openair_tech@eurecom.fr
|
|
OpenAirInterface Dev : openair4g-devel@eurecom.fr
|
|
|
|
Address : Eurecom, Compus SophiaTech 450, route des chappes, 06451 Biot, France.
|
|
|
|
*******************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <inttypes.h>
|
|
|
|
#include "assertions.h"
|
|
#include "conversions.h"
|
|
|
|
#include "sgw_lite_messages_types.h"
|
|
|
|
#include "NwGtpv2c.h"
|
|
#include "NwGtpv2cIe.h"
|
|
#include "NwGtpv2cMsg.h"
|
|
#include "NwGtpv2cMsgParser.h"
|
|
|
|
#include "s11_common.h"
|
|
#include "s11_ie_formatter.h"
|
|
|
|
NwRcT s11_imsi_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
Imsi_t *imsi;
|
|
uint8_t i;
|
|
uint8_t mask = 0x0F;
|
|
uint8_t imsi_length = 2 * ieLength;
|
|
|
|
DevAssert(arg != NULL);
|
|
imsi = (Imsi_t *)arg;
|
|
|
|
for (i = 0; i < ieLength * 2; i++) {
|
|
if (mask == 0x0F) {
|
|
imsi->digit[i] = (ieValue[i / 2] & (mask));
|
|
} else {
|
|
imsi->digit[i] = (ieValue[i / 2] & (mask)) >> 4;
|
|
}
|
|
|
|
imsi->digit[i] += '0';
|
|
mask = ~mask;
|
|
}
|
|
|
|
if (imsi->digit[imsi_length - 1] == (0x0f + '0')) {
|
|
imsi->digit[imsi_length - 1] = 0;
|
|
imsi_length--;
|
|
}
|
|
|
|
imsi->length = imsi_length;
|
|
|
|
S11_DEBUG("\t- IMSI length %d\n", imsi->length);
|
|
S11_DEBUG("\t- value %*s\n", imsi->length, imsi->digit);
|
|
|
|
return NW_OK;
|
|
}
|
|
|
|
int s11_imsi_ie_set(NwGtpv2cMsgHandleT *msg, const Imsi_t *imsi)
|
|
{
|
|
uint8_t *temp = NULL;
|
|
uint8_t imsi_length, i;
|
|
NwRcT rc;
|
|
|
|
DevAssert(msg != NULL);
|
|
DevAssert(imsi != NULL);
|
|
|
|
/* In case of odd/even imsi */
|
|
imsi_length = imsi->length % 2 == 0 ? imsi->length / 2 : imsi->length / 2 + 1;
|
|
|
|
temp = calloc(imsi_length, sizeof(uint8_t));
|
|
|
|
DevAssert(temp != NULL);
|
|
|
|
for (i = 0; i < imsi->length; i++) {
|
|
temp[i / 2] |= ((imsi->digit[i] - '0') & 0x0F) << (i % 2 ? 4 : 0);
|
|
}
|
|
|
|
rc = nwGtpv2cMsgAddIe(*msg, NW_GTPV2C_IE_IMSI, imsi_length, 0, temp);
|
|
DevAssert(NW_OK == rc);
|
|
|
|
free(temp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
NwRcT s11_msisdn_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
Msisdn_t *msisdn;
|
|
uint8_t i;
|
|
uint8_t mask = 0x0F;
|
|
uint8_t msisdn_length = 2 * ieLength;
|
|
|
|
DevAssert(arg != NULL);
|
|
msisdn = (Msisdn_t *)arg;
|
|
|
|
for (i = 0; i < ieLength * 2; i++) {
|
|
if (mask == 0x0F) {
|
|
msisdn->digit[i] = (ieValue[i / 2] & (mask));
|
|
} else {
|
|
msisdn->digit[i] = (ieValue[i / 2] & (mask)) >> 4;
|
|
}
|
|
|
|
msisdn->digit[i] += '0';
|
|
mask = ~mask;
|
|
}
|
|
|
|
if (msisdn->digit[msisdn_length - 1] == (0x0f + '0')) {
|
|
msisdn->digit[msisdn_length - 1] = 0;
|
|
msisdn_length--;
|
|
}
|
|
|
|
msisdn->length = msisdn_length;
|
|
|
|
S11_DEBUG("\t- MSISDN length %d\n", msisdn->length);
|
|
S11_DEBUG("\t- value %*s\n", msisdn->length, (char *)msisdn->digit);
|
|
|
|
return NW_OK;
|
|
}
|
|
|
|
NwRcT s11_mei_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
Mei_t *mei = (Mei_t *)arg;
|
|
|
|
DevAssert(mei != NULL);
|
|
|
|
|
|
|
|
return NW_OK;
|
|
}
|
|
|
|
NwRcT s11_pdn_type_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
pdn_type_t *pdn_type = (pdn_type_t *)arg;
|
|
|
|
DevAssert(pdn_type != NULL);
|
|
|
|
if (*ieValue == 1) {
|
|
/* Only IPv4 */
|
|
*pdn_type = IPv4;
|
|
} else if (*ieValue == 2) {
|
|
/* Only IPv6 */
|
|
*pdn_type = IPv6;
|
|
} else if (*ieValue == 3) {
|
|
/* IPv4 and/or IPv6 */
|
|
*pdn_type = IPv4_AND_v6;
|
|
} else {
|
|
S11_ERROR("Received unknown value for PDN Type: %u\n", *ieValue);
|
|
return NW_GTPV2C_IE_INCORRECT;
|
|
}
|
|
|
|
S11_DEBUG("\t- PDN type %u\n", *pdn_type);
|
|
|
|
return NW_OK;
|
|
}
|
|
|
|
int s11_pdn_type_ie_set(NwGtpv2cMsgHandleT *msg, const pdn_type_t *pdn_type)
|
|
{
|
|
NwRcT rc;
|
|
uint8_t value;
|
|
|
|
DevAssert(pdn_type != NULL);
|
|
DevAssert(msg != NULL);
|
|
|
|
switch (*pdn_type) {
|
|
case IPv4:
|
|
value = 1;
|
|
break;
|
|
|
|
case IPv6:
|
|
value = 2;
|
|
break;
|
|
|
|
case IPv4_AND_v6:
|
|
case IPv4_OR_v6:
|
|
value = 3;
|
|
break;
|
|
|
|
default:
|
|
S11_ERROR("Invalid PDN type received: %d\n", *pdn_type);
|
|
return -1;
|
|
}
|
|
|
|
rc = nwGtpv2cMsgAddIe(*msg, NW_GTPV2C_IE_PDN_TYPE, 1, 0, (NwU8T*)&value);
|
|
DevAssert(NW_OK == rc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
NwRcT s11_rat_type_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
rat_type_t *rat_type = (rat_type_t *)arg;
|
|
|
|
DevAssert(rat_type != NULL);
|
|
|
|
switch (*ieValue) {
|
|
case 1:
|
|
*rat_type = RAT_UTRAN;
|
|
break;
|
|
|
|
case 2:
|
|
*rat_type = RAT_GERAN;
|
|
break;
|
|
|
|
case 3:
|
|
*rat_type = RAT_WLAN;
|
|
break;
|
|
|
|
case 4:
|
|
*rat_type = RAT_GAN;
|
|
break;
|
|
|
|
case 5:
|
|
*rat_type = RAT_HSPA_EVOLUTION;
|
|
break;
|
|
|
|
case 6:
|
|
*rat_type = RAT_EUTRAN;
|
|
break;
|
|
|
|
default:
|
|
S11_ERROR("Can't map GTP RAT type %u to EPC definition\n"
|
|
"\tCheck TS.29.274 #8.17 for possible values\n", *ieValue);
|
|
return NW_GTPV2C_IE_INCORRECT;
|
|
}
|
|
|
|
S11_DEBUG("\t- RAT type (%d): %d\n", *ieValue, *rat_type);
|
|
|
|
return NW_OK;
|
|
}
|
|
|
|
int s11_rat_type_ie_set(NwGtpv2cMsgHandleT *msg, const rat_type_t *rat_type)
|
|
{
|
|
NwRcT rc;
|
|
uint8_t value;
|
|
|
|
DevAssert(rat_type != NULL);
|
|
DevAssert(msg != NULL);
|
|
|
|
switch (*rat_type) {
|
|
case RAT_UTRAN:
|
|
value = 1;
|
|
break;
|
|
|
|
case RAT_GERAN:
|
|
value = 2;
|
|
break;
|
|
|
|
case RAT_WLAN:
|
|
value = 3;
|
|
break;
|
|
|
|
case RAT_GAN:
|
|
value = 4;
|
|
break;
|
|
|
|
case RAT_HSPA_EVOLUTION:
|
|
value = 5;
|
|
break;
|
|
|
|
case RAT_EUTRAN:
|
|
value = 6;
|
|
break;
|
|
|
|
default:
|
|
S11_ERROR("Can't map RAT type %d to GTP RAT type\n"
|
|
"\tCheck TS.29.274 #8.17 for possible values\n", *rat_type);
|
|
return -1;
|
|
}
|
|
|
|
rc = nwGtpv2cMsgAddIe(*msg, NW_GTPV2C_IE_RAT_TYPE, 1, 0, (NwU8T*)&value);
|
|
DevAssert(NW_OK == rc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int s11_ebi_ie_set(NwGtpv2cMsgHandleT *msg, const unsigned ebi)
|
|
{
|
|
NwRcT rc;
|
|
uint8_t value = 0;
|
|
|
|
value = ebi & 0x0F;
|
|
|
|
rc = nwGtpv2cMsgAddIe(*msg, NW_GTPV2C_IE_EBI, 1, 0,
|
|
&value);
|
|
DevAssert(NW_OK == rc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
NwRcT s11_ebi_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
uint8_t *ebi = (uint8_t *)arg;
|
|
|
|
DevAssert(ebi != NULL);
|
|
|
|
*ebi = ieValue[0] & 0x0F;
|
|
|
|
S11_DEBUG("\t- EBI %u\n", *ebi);
|
|
|
|
return NW_OK;
|
|
}
|
|
|
|
NwRcT s11_cause_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
SGWCause_t *cause = (SGWCause_t *)arg;
|
|
|
|
DevAssert(cause != NULL);
|
|
|
|
*cause = ieValue[0];
|
|
|
|
S11_DEBUG("\t- Cause %u\n", *cause);
|
|
|
|
return NW_OK;
|
|
}
|
|
|
|
int s11_cause_ie_set(NwGtpv2cMsgHandleT *msg,
|
|
const gtp_cause_t *cause)
|
|
{
|
|
NwRcT rc;
|
|
uint8_t value[6];
|
|
|
|
DevAssert(msg != NULL);
|
|
DevAssert(cause != NULL);
|
|
|
|
value[0] = cause->cause_value;
|
|
value[1] = ((cause->pce & 0x1) << 2) | ((cause->bce & 0x1) << 1) | (cause->cs & 0x1);
|
|
|
|
if (cause->offending_ie_type != 0) {
|
|
value[2] = cause->offending_ie_type;
|
|
value[3] = (cause->offending_ie_length & 0xFF00) >> 8;
|
|
value[4] = cause->offending_ie_length & 0x00FF;
|
|
value[5] = cause->offending_ie_instance & 0x0F;
|
|
rc = nwGtpv2cMsgAddIe(*msg, NW_GTPV2C_IE_CAUSE, 6, 0, value);
|
|
} else {
|
|
rc = nwGtpv2cMsgAddIe(*msg, NW_GTPV2C_IE_CAUSE, 2, 0, value);
|
|
}
|
|
|
|
DevAssert(NW_OK == rc);
|
|
|
|
return rc == NW_OK ? 0 : -1;
|
|
}
|
|
|
|
NwRcT s11_bearer_context_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
bearer_to_create_t *bearer_to_create = (bearer_to_create_t *)arg;
|
|
uint8_t read = 0;
|
|
NwRcT rc;
|
|
|
|
DevAssert(bearer_to_create != NULL);
|
|
|
|
while (ieLength > read) {
|
|
NwGtpv2cIeTlvT *ie_p;
|
|
|
|
ie_p = (NwGtpv2cIeTlvT*)&ieValue[read];
|
|
|
|
switch (ie_p->t) {
|
|
case NW_GTPV2C_IE_EBI:
|
|
rc = s11_ebi_ie_get(ie_p->t, ie_p->l, ie_p->i,
|
|
&ieValue[read + sizeof(NwGtpv2cIeTlvT)],
|
|
&bearer_to_create->eps_bearer_id);
|
|
DevAssert(NW_OK == rc);
|
|
break;
|
|
|
|
case NW_GTPV2C_IE_BEARER_LEVEL_QOS:
|
|
break;
|
|
|
|
default:
|
|
S11_ERROR("Received unexpected IE %u\n", ie_p->t);
|
|
return NW_GTPV2C_IE_INCORRECT;
|
|
}
|
|
|
|
read += (ntohs(ie_p->l) + sizeof(NwGtpv2cIeTlvT));
|
|
}
|
|
|
|
return NW_OK;
|
|
}
|
|
|
|
int s11_bearer_context_to_create_ie_set(
|
|
NwGtpv2cMsgHandleT *msg, const bearer_to_create_t *bearer_to_create)
|
|
{
|
|
NwRcT rc;
|
|
|
|
DevAssert(msg != NULL);
|
|
DevAssert(bearer_to_create != NULL);
|
|
|
|
/* Start section for grouped IE: bearer context to create */
|
|
rc = nwGtpv2cMsgGroupedIeStart(*msg, NW_GTPV2C_IE_BEARER_CONTEXT,
|
|
NW_GTPV2C_IE_INSTANCE_ZERO);
|
|
DevAssert(NW_OK == rc);
|
|
|
|
s11_ebi_ie_set(msg, bearer_to_create->eps_bearer_id);
|
|
|
|
/* End section for grouped IE: bearer context to create */
|
|
rc = nwGtpv2cMsgGroupedIeEnd(*msg);
|
|
DevAssert(NW_OK == rc);
|
|
return 0;
|
|
}
|
|
|
|
NwRcT s11_bearer_context_to_modifiy_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
bearer_context_to_modify_t *bearer_to_modify = (bearer_context_to_modify_t *)arg;
|
|
uint8_t read = 0;
|
|
NwRcT rc;
|
|
|
|
DevAssert(bearer_to_modify != NULL);
|
|
|
|
while (ieLength > read) {
|
|
NwGtpv2cIeTlvT *ie_p;
|
|
|
|
ie_p = (NwGtpv2cIeTlvT*)&ieValue[read];
|
|
|
|
switch (ie_p->t) {
|
|
case NW_GTPV2C_IE_EBI:
|
|
rc = s11_ebi_ie_get(ie_p->t, ie_p->l, ie_p->i,
|
|
&ieValue[read + sizeof(NwGtpv2cIeTlvT)],
|
|
&bearer_to_modify->eps_bearer_id);
|
|
DevAssert(NW_OK == rc);
|
|
break;
|
|
|
|
case NW_GTPV2C_IE_FTEID:
|
|
rc = s11_fteid_ie_get(ie_p->t, ie_p->l, ie_p->i,
|
|
&ieValue[read + sizeof(NwGtpv2cIeTlvT)],
|
|
&bearer_to_modify->s1_eNB_fteid);
|
|
break;
|
|
|
|
default:
|
|
S11_ERROR("Received unexpected IE %u\n", ie_p->t);
|
|
return NW_GTPV2C_IE_INCORRECT;
|
|
}
|
|
|
|
read += (ntohs(ie_p->l) + sizeof(NwGtpv2cIeTlvT));
|
|
}
|
|
|
|
return NW_OK;
|
|
}
|
|
|
|
NwRcT s11_bearer_context_created_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
bearer_context_created_t *bearer_created = (bearer_context_created_t *)arg;
|
|
uint8_t read = 0;
|
|
NwRcT rc;
|
|
|
|
DevAssert(bearer_created != NULL);
|
|
|
|
while (ieLength > read) {
|
|
NwGtpv2cIeTlvT *ie_p;
|
|
|
|
ie_p = (NwGtpv2cIeTlvT*)&ieValue[read];
|
|
|
|
switch (ie_p->t) {
|
|
case NW_GTPV2C_IE_EBI:
|
|
rc = s11_ebi_ie_get(ie_p->t, ie_p->l, ie_p->i,
|
|
&ieValue[read + sizeof(NwGtpv2cIeTlvT)],
|
|
&bearer_created->eps_bearer_id);
|
|
DevAssert(NW_OK == rc);
|
|
break;
|
|
|
|
case NW_GTPV2C_IE_FTEID:
|
|
rc = s11_fteid_ie_get(ie_p->t, ie_p->l, ie_p->i,
|
|
&ieValue[read + sizeof(NwGtpv2cIeTlvT)],
|
|
&bearer_created->s1u_sgw_fteid);
|
|
break;
|
|
|
|
case NW_GTPV2C_IE_CAUSE:
|
|
rc = s11_cause_ie_get(ie_p->t, ie_p->l, ie_p->i,
|
|
&ieValue[read + sizeof(NwGtpv2cIeTlvT)],
|
|
&bearer_created->cause);
|
|
break;
|
|
|
|
default:
|
|
S11_ERROR("Received unexpected IE %u\n", ie_p->t);
|
|
return NW_GTPV2C_IE_INCORRECT;
|
|
}
|
|
|
|
read += (ntohs(ie_p->l) + sizeof(NwGtpv2cIeTlvT));
|
|
}
|
|
|
|
return NW_OK;
|
|
}
|
|
|
|
int s11_bearer_context_created_ie_set(
|
|
NwGtpv2cMsgHandleT *msg, const bearer_context_created_t *bearer)
|
|
{
|
|
NwRcT rc;
|
|
|
|
DevAssert(msg != NULL);
|
|
DevAssert(bearer != NULL);
|
|
|
|
/* Start section for grouped IE: bearer context created */
|
|
rc = nwGtpv2cMsgGroupedIeStart(*msg, NW_GTPV2C_IE_BEARER_CONTEXT,
|
|
NW_GTPV2C_IE_INSTANCE_ZERO);
|
|
DevAssert(NW_OK == rc);
|
|
|
|
s11_ebi_ie_set(msg, bearer->eps_bearer_id);
|
|
|
|
rc = nwGtpv2cMsgAddIeCause(*msg, NW_GTPV2C_IE_INSTANCE_ZERO, bearer->cause,
|
|
0, 0, 0);
|
|
DevAssert(NW_OK == rc);
|
|
|
|
rc = nwGtpv2cMsgAddIeFteid(*msg, NW_GTPV2C_IE_INSTANCE_ZERO,
|
|
bearer->s1u_sgw_fteid.interface_type,
|
|
bearer->s1u_sgw_fteid.teid,
|
|
bearer->s1u_sgw_fteid.ipv4 ? htonl(bearer->s1u_sgw_fteid.ipv4_address) : 0,
|
|
bearer->s1u_sgw_fteid.ipv6 ? (NwU8T*)bearer->s1u_sgw_fteid.ipv6_address : NULL);
|
|
DevAssert(NW_OK == rc);
|
|
|
|
/* End section for grouped IE: bearer context created */
|
|
rc = nwGtpv2cMsgGroupedIeEnd(*msg);
|
|
DevAssert(NW_OK == rc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* This IE shall be included in the E-UTRAN initial attach,
|
|
* PDP Context Activation and UE Requested PDN connectivity procedures.
|
|
* This IE denotes the most stringent restriction as required
|
|
* by any already active bearer context. If there are no already active bearer
|
|
* contexts, this value is set to the least restrictive type.
|
|
*/
|
|
int s11_apn_restriction_ie_set(
|
|
NwGtpv2cMsgHandleT *msg, const uint8_t apn_restriction)
|
|
{
|
|
NwRcT rc;
|
|
|
|
DevAssert(msg != NULL);
|
|
|
|
rc = nwGtpv2cMsgAddIe(*msg, NW_GTPV2C_IE_APN_RESTRICTION, 1, 0,
|
|
(NwU8T *)&apn_restriction);
|
|
DevAssert(NW_OK == rc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
NwRcT s11_serving_network_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
ServingNetwork_t *serving_net = (ServingNetwork_t *)arg;
|
|
|
|
DevAssert(serving_net != NULL);
|
|
|
|
serving_net->mcc[1] = (ieValue[0] & 0xF0) >> 4;
|
|
serving_net->mcc[0] = (ieValue[0] & 0x0F);
|
|
serving_net->mcc[2] = (ieValue[1] & 0x0F);
|
|
|
|
if ((ieValue[1] & 0xF0) == 0xF0) {
|
|
/* Two digits MNC */
|
|
serving_net->mnc[0] = 0;
|
|
serving_net->mnc[2] = (ieValue[2] & 0xF0) >> 4;
|
|
serving_net->mnc[1] = (ieValue[2] & 0x0F);
|
|
} else {
|
|
serving_net->mnc[0] = (ieValue[1] & 0xF0) >> 4;
|
|
serving_net->mnc[2] = (ieValue[2] & 0xF0) >> 4;
|
|
serving_net->mnc[1] = (ieValue[2] & 0x0F);
|
|
}
|
|
|
|
S11_DEBUG("\t- Serving network %d.%d\n",
|
|
serving_net->mcc[0] * 100 + serving_net->mcc[1] * 10
|
|
+ serving_net->mcc[2],
|
|
serving_net->mnc[0] * 100 + serving_net->mnc[1] * 10
|
|
+ serving_net->mnc[2]);
|
|
|
|
return NW_OK;
|
|
}
|
|
|
|
int s11_serving_network_ie_set(
|
|
NwGtpv2cMsgHandleT *msg,
|
|
const ServingNetwork_t *serving_network)
|
|
{
|
|
NwRcT rc;
|
|
uint8_t value[3];
|
|
|
|
DevAssert(msg != NULL);
|
|
DevAssert(serving_network != NULL);
|
|
|
|
/* MCC Decimal | MCC Hundreds */
|
|
value[0] = ((serving_network->mcc[1] & 0x0F) << 4) |
|
|
(serving_network->mcc[2] & 0x0F);
|
|
value[1] = serving_network->mcc[0] & 0x0F;
|
|
|
|
if ((serving_network->mnc[0] & 0xF) == 0xF) {
|
|
/* Only two digits */
|
|
value[1] |= 0xF0;
|
|
value[2] = ((serving_network->mnc[2] & 0x0F) << 4) |
|
|
(serving_network->mnc[1] & 0x0F);
|
|
} else {
|
|
value[1] |= (serving_network->mnc[2] & 0x0F) << 4;
|
|
value[2] = ((serving_network->mnc[1] & 0x0F) << 4) |
|
|
(serving_network->mnc[0] & 0x0F);
|
|
}
|
|
|
|
rc = nwGtpv2cMsgAddIe(*msg, NW_GTPV2C_IE_SERVING_NETWORK, 3, 0, value);
|
|
DevAssert(NW_OK == rc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
NwRcT s11_fteid_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
uint8_t offset = 0;
|
|
FTeid_t *fteid = (FTeid_t *)arg;
|
|
|
|
DevAssert(fteid != NULL);
|
|
|
|
fteid->ipv4 = (ieValue[0] & 0x80) >> 7;
|
|
fteid->ipv6 = (ieValue[0] & 0x40) >> 6;
|
|
|
|
fteid->interface_type = ieValue[0] & 0x1F;
|
|
S11_DEBUG("\t- F-TEID type %d\n", fteid->interface_type);
|
|
|
|
/* Copy the TEID or GRE key */
|
|
fteid->teid = ntoh_int32_buf(&ieValue[1]);
|
|
|
|
S11_DEBUG("\t- TEID/GRE %08x\n", fteid->teid);
|
|
|
|
if (fteid->ipv4 == 1) {
|
|
/* IPv4 present: copy the 4 bytes */
|
|
memcpy(&fteid->ipv4_address, &ieValue[5], 4);
|
|
offset = 4;
|
|
|
|
S11_DEBUG("\t- IPv4 addr "IPV4_ADDR"\n",
|
|
IPV4_ADDR_FORMAT(fteid->ipv4_address));
|
|
}
|
|
|
|
if (fteid->ipv6 == 1) {
|
|
char ipv6_ascii[40];
|
|
/* IPv6 present: copy the 16 bytes
|
|
* WARNING: if Ipv4 is present, 4 bytes of offset should be applied
|
|
*/
|
|
memcpy(fteid->ipv6_address, &ieValue[5 + offset], 16);
|
|
|
|
inet_ntop(AF_INET6, fteid->ipv6_address, ipv6_ascii, 40);
|
|
S11_DEBUG("\t- IPv6 addr %s\n", ipv6_ascii);
|
|
}
|
|
|
|
return NW_OK;
|
|
}
|
|
|
|
NwRcT s11_paa_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
uint8_t offset = 0;
|
|
PAA_t *paa = (PAA_t *)arg;
|
|
|
|
DevAssert(paa != NULL);
|
|
|
|
paa->pdn_type = ieValue[0] & 0x07;
|
|
S11_DEBUG("\t- PAA type %d\n", paa->pdn_type);
|
|
|
|
if (paa->pdn_type & 0x2) {
|
|
char ipv6_ascii[40];
|
|
/* IPv6 present: copy the 16 bytes
|
|
* WARNING: if both ipv4 and ipv6 are present,
|
|
* 17 bytes of offset should be applied for ipv4
|
|
* NOTE: an ipv6 prefix length is prepend
|
|
* NOTE: in Rel.8 the prefix length has a default value of /64
|
|
*/
|
|
paa->ipv6_prefix_length = ieValue[1];
|
|
memcpy(paa->ipv6_address, &ieValue[2], 16);
|
|
|
|
inet_ntop(AF_INET6, paa->ipv6_address, ipv6_ascii, 40);
|
|
S11_DEBUG("\t- IPv6 addr %s/%u\n", ipv6_ascii, paa->ipv6_prefix_length);
|
|
}
|
|
|
|
if (paa->pdn_type == 3) {
|
|
offset = 17;
|
|
}
|
|
|
|
if (paa->pdn_type & 0x1) {
|
|
memcpy(paa->ipv4_address, &ieValue[1 + offset], 4);
|
|
|
|
S11_DEBUG("\t- IPv4 addr "IPV4_ADDR"\n",
|
|
paa->ipv4_address[0], paa->ipv4_address[1],
|
|
paa->ipv4_address[2], paa->ipv4_address[3]);
|
|
}
|
|
|
|
paa->pdn_type -= 1;
|
|
return NW_OK;
|
|
}
|
|
|
|
int s11_paa_ie_set(NwGtpv2cMsgHandleT *msg, const PAA_t *paa)
|
|
{
|
|
/* ipv4 address = 4 + ipv6 address = 16 + ipv6 prefix length = 1
|
|
* + pdn_type = 1
|
|
* = maximum of 22 bytes
|
|
*/
|
|
uint8_t temp[22];
|
|
uint8_t pdn_type;
|
|
uint8_t offset = 0;
|
|
NwRcT rc;
|
|
|
|
DevAssert(paa != NULL);
|
|
|
|
pdn_type = paa->pdn_type + 1;
|
|
|
|
temp[offset] = pdn_type;
|
|
offset++;
|
|
|
|
if (pdn_type & 0x2) {
|
|
/* If ipv6 or ipv4v6 present */
|
|
temp[1] = paa->ipv6_prefix_length;
|
|
memcpy(&temp[2], paa->ipv6_address, 16);
|
|
offset += 17;
|
|
}
|
|
|
|
if (pdn_type & 0x1) {
|
|
memcpy(&temp[offset], paa->ipv4_address, 4);
|
|
offset += 4;
|
|
}
|
|
|
|
rc = nwGtpv2cMsgAddIe(*msg, NW_GTPV2C_IE_PAA, offset, 0,
|
|
temp);
|
|
DevAssert(NW_OK == rc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* The encoding of the APN shall follow the Name Syntax defined in RFC 2181,
|
|
* RFC 1035 and RFC 1123. The APN consists of one or more labels. Each label
|
|
* is coded as a one octet length field followed by that number of octets
|
|
* coded as 8 bit ASCII characters. Following RFC 1035 the labels shall consist
|
|
* only of the alphabetic characters (A-Z and a-z), digits (0-9)
|
|
* and the hyphen (-).
|
|
*/
|
|
NwRcT s11_apn_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
uint8_t read = 1;
|
|
uint8_t word_length;
|
|
char *apn = (char *)arg;
|
|
|
|
DevAssert(apn != NULL);
|
|
|
|
DevCheck(ieLength <= APN_MAX_LENGTH, ieLength, APN_MAX_LENGTH, 0);
|
|
|
|
word_length = ieValue[0];
|
|
|
|
while (read < ieLength) {
|
|
if (word_length > 0) {
|
|
apn[read - 1] = ieValue[read];
|
|
word_length--;
|
|
} else {
|
|
/* This is not an alphanumeric character */
|
|
word_length = ieValue[read];
|
|
/* Replace the length attribute by '.' */
|
|
apn[read - 1] = '.';
|
|
}
|
|
|
|
read ++;
|
|
}
|
|
|
|
apn[read - 1] = '\0';
|
|
|
|
S11_DEBUG("\t- APN %s\n", apn);
|
|
|
|
return NW_OK;
|
|
}
|
|
|
|
int s11_apn_ie_set(NwGtpv2cMsgHandleT *msg, const char *apn)
|
|
{
|
|
NwRcT rc;
|
|
uint8_t *value;
|
|
uint8_t apn_length;
|
|
uint8_t offset = 0;
|
|
uint8_t *last_size;
|
|
uint8_t word_length = 0;
|
|
|
|
DevAssert(apn != NULL);
|
|
DevAssert(msg != NULL);
|
|
|
|
apn_length = strlen(apn);
|
|
|
|
value = calloc(apn_length + 1, sizeof(uint8_t));
|
|
last_size = &value[0];
|
|
|
|
while (apn[offset]) {
|
|
/* We replace the . by the length of the word */
|
|
if (apn[offset] == '.') {
|
|
*last_size = word_length;
|
|
word_length = 0;
|
|
last_size = &value[offset + 1];
|
|
} else {
|
|
word_length++;
|
|
value[offset + 1] = apn[offset];
|
|
}
|
|
|
|
offset++;
|
|
}
|
|
|
|
*last_size = word_length;
|
|
|
|
rc = nwGtpv2cMsgAddIe(*msg, NW_GTPV2C_IE_APN, apn_length + 1, 0, value);
|
|
DevAssert(NW_OK == rc);
|
|
|
|
free(value);
|
|
|
|
return 0;
|
|
}
|
|
|
|
NwRcT s11_ambr_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
ambr_t *ambr = (ambr_t *)arg;
|
|
|
|
DevAssert(ambr != NULL);
|
|
|
|
ambr->br_ul = ntoh_int32_buf(&ieValue[0]);
|
|
ambr->br_dl = ntoh_int32_buf(&ieValue[4]);
|
|
|
|
S11_DEBUG("\t- AMBR UL %"PRIu64"\n", ambr->br_ul);
|
|
S11_DEBUG("\t- AMBR DL %"PRIu64"\n", ambr->br_dl);
|
|
|
|
return NW_OK;
|
|
}
|
|
|
|
NwRcT s11_uli_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
Uli_t *uli = (Uli_t *)arg;
|
|
|
|
DevAssert(uli != NULL);
|
|
|
|
uli->present = ieValue[0];
|
|
|
|
if (uli->present & ULI_CGI) {
|
|
|
|
}
|
|
|
|
return NW_OK;
|
|
}
|
|
|
|
int s11_bearer_qos_ie_set(NwGtpv2cMsgHandleT *msg, const BearerQOS_t *bearer_qos)
|
|
{
|
|
NwRcT rc;
|
|
uint8_t value[18];
|
|
|
|
DevAssert(msg != NULL);
|
|
DevAssert(bearer_qos != NULL);
|
|
|
|
value[0] = (bearer_qos->pci << 6) | (bearer_qos->pl << 2) | (bearer_qos->pvi);
|
|
value[1] = bearer_qos->qci;
|
|
|
|
/* TODO: check endianness */
|
|
memcpy(&value[2], &bearer_qos->mbr.br_ul, 4);
|
|
memcpy(&value[6], &bearer_qos->mbr.br_dl, 4);
|
|
memcpy(&value[10], &bearer_qos->gbr.br_ul, 4);
|
|
memcpy(&value[14], &bearer_qos->gbr.br_dl, 4);
|
|
|
|
rc = nwGtpv2cMsgAddIe(*msg, NW_GTPV2C_IE_BEARER_LEVEL_QOS, 18, 0, value);
|
|
DevAssert(NW_OK == rc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
NwRcT s11_ip_address_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
gtp_ip_address_t *ip_address = (gtp_ip_address_t *)arg;
|
|
|
|
DevAssert(ip_address != NULL);
|
|
|
|
if (ieLength == 4) {
|
|
/* This is an IPv4 Address */
|
|
ip_address->present = GTP_IP_ADDR_v4;
|
|
memcpy(ip_address->address.v4, ieValue, 4);
|
|
} else if (ieLength == 16) {
|
|
/* This is an IPv6 Address */
|
|
ip_address->present = GTP_IP_ADDR_v6;
|
|
memcpy(ip_address->address.v6, ieValue, 16);
|
|
} else {
|
|
/* Length doesn't lie in possible values */
|
|
return NW_GTPV2C_IE_INCORRECT;
|
|
}
|
|
|
|
return NW_OK;
|
|
}
|
|
|
|
int s11_ip_address_ie_set(NwGtpv2cMsgHandleT *msg,
|
|
const gtp_ip_address_t *ip_address)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
NwRcT s11_delay_value_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
DelayValue_t *delay_value = (DelayValue_t *)arg;
|
|
|
|
DevAssert(arg != NULL);
|
|
|
|
if (ieLength != 1) {
|
|
return NW_GTPV2C_IE_INCORRECT;
|
|
}
|
|
|
|
*delay_value = ieValue[0];
|
|
|
|
S11_DEBUG("\t - Delay Value %u\n", *delay_value);
|
|
|
|
return NW_OK;
|
|
}
|
|
|
|
int s11_delay_value_ie_set(NwGtpv2cMsgHandleT *msg,
|
|
const DelayValue_t *delay_value)
|
|
{
|
|
uint8_t value;
|
|
NwRcT rc;
|
|
|
|
DevAssert(msg != NULL);
|
|
DevAssert(delay_value != NULL);
|
|
|
|
value = *delay_value;
|
|
|
|
rc = nwGtpv2cMsgAddIe(*msg, NW_GTPV2C_IE_DELAY_VALUE, 1, 0, (NwU8T*)&value);
|
|
DevAssert(NW_OK == rc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
NwRcT s11_ue_time_zone_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
UETimeZone_t *ue_time_zone = (UETimeZone_t *)arg;
|
|
|
|
DevAssert(ue_time_zone != NULL);
|
|
|
|
if (ieLength != 2) {
|
|
return NW_GTPV2C_IE_INCORRECT;
|
|
}
|
|
|
|
ue_time_zone->time_zone = ieValue[0];
|
|
ue_time_zone->daylight_saving_time = ieValue[1] & 0x03;
|
|
|
|
S11_DEBUG("\t - Time Zone %u\n", ue_time_zone->time_zone);
|
|
S11_DEBUG("\t - Daylight SVT %u\n", ue_time_zone->daylight_saving_time);
|
|
|
|
return NW_OK;
|
|
}
|
|
|
|
int s11_ue_time_zone_ie_set(NwGtpv2cMsgHandleT *msg,
|
|
const UETimeZone_t *ue_time_zone)
|
|
{
|
|
uint8_t value[2];
|
|
NwRcT rc;
|
|
|
|
DevAssert(msg != NULL);
|
|
DevAssert(ue_time_zone != NULL);
|
|
|
|
value[0] = ue_time_zone->time_zone;
|
|
value[1] = ue_time_zone->daylight_saving_time;
|
|
|
|
rc = nwGtpv2cMsgAddIe(*msg, NW_GTPV2C_IE_UE_TIME_ZONE, 2, 0, value);
|
|
DevAssert(NW_OK == rc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
NwRcT s11_target_identification_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
target_identification_t *target_identification = (target_identification_t *)arg;
|
|
|
|
DevAssert(target_identification != NULL);
|
|
|
|
target_identification->target_type = ieValue[0];
|
|
|
|
switch (target_identification->target_type) {
|
|
case TARGET_ID_RNC_ID: {
|
|
target_identification->target_id.rnc_id.lac = (ieValue[4] << 8) |
|
|
ieValue[5];
|
|
target_identification->target_id.rnc_id.rac = ieValue[6];
|
|
|
|
if (ieLength == 11) {
|
|
/* Extended RNC id */
|
|
target_identification->target_id.rnc_id.rnc_id = (ieValue[7] << 24) |
|
|
(ieValue[8] << 16) | (ieValue[9] << 8) | (ieValue[10]);
|
|
} else if (ieLength == 9) {
|
|
/* Normal RNC id */
|
|
target_identification->target_id.rnc_id.rnc_id = (ieValue[7] << 8) |
|
|
ieValue[8];
|
|
} else {
|
|
/* This case is not possible */
|
|
return NW_GTPV2C_IE_INCORRECT;
|
|
}
|
|
|
|
S11_DEBUG("\t\t- LAC 0x%04x\n", target_identification->target_id.rnc_id.lac);
|
|
S11_DEBUG("\t\t- RAC 0x%02x\n", target_identification->target_id.rnc_id.rac);
|
|
S11_DEBUG("\t\t- RNC 0x%08x\n", target_identification->target_id.rnc_id.rnc_id);
|
|
}
|
|
break;
|
|
|
|
case TARGET_ID_MACRO_ENB_ID: {
|
|
if (ieLength != 9) {
|
|
return NW_GTPV2C_IE_INCORRECT;
|
|
}
|
|
|
|
target_identification->target_id.macro_enb_id.enb_id = ((ieValue[4] & 0x0F) << 16) |
|
|
(ieValue[5] << 8) | ieValue[6];
|
|
target_identification->target_id.macro_enb_id.tac = (ieValue[7] << 8) | ieValue[8];
|
|
S11_DEBUG("\t\t- ENB Id 0x%06x\n", target_identification->target_id.macro_enb_id.enb_id);
|
|
S11_DEBUG("\t\t- TAC 0x%04x\n", target_identification->target_id.macro_enb_id.tac);
|
|
}
|
|
break;
|
|
|
|
case TARGET_ID_HOME_ENB_ID: {
|
|
if (ieLength != 10) {
|
|
return NW_GTPV2C_IE_INCORRECT;
|
|
}
|
|
|
|
target_identification->target_id.home_enb_id.enb_id = ((ieValue[4] & 0x0F) << 14) |
|
|
(ieValue[5] << 16) | (ieValue[6] << 8) | ieValue[7];
|
|
target_identification->target_id.home_enb_id.tac = (ieValue[8] << 8) | ieValue[9];
|
|
S11_DEBUG("\t\t- ENB Id 0x%07x\n", target_identification->target_id.home_enb_id.enb_id);
|
|
S11_DEBUG("\t\t- TAC 0x%04x\n", target_identification->target_id.home_enb_id.tac);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return NW_GTPV2C_IE_INCORRECT;
|
|
}
|
|
|
|
return NW_OK;
|
|
}
|
|
|
|
NwRcT s11_bearer_flags_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
bearer_flags_t *bearer_flags = (bearer_flags_t *)arg;
|
|
|
|
DevAssert(arg != NULL);
|
|
|
|
if (ieLength != 1) {
|
|
return NW_GTPV2C_IE_INCORRECT;
|
|
}
|
|
|
|
bearer_flags->ppc = ieValue[0] & 0x01;
|
|
bearer_flags->vb = ieValue[0] & 0x02;
|
|
|
|
return NW_OK;
|
|
}
|
|
|
|
int s11_bearer_flags_ie_set(NwGtpv2cMsgHandleT *msg,
|
|
const bearer_flags_t *bearer_flags)
|
|
{
|
|
NwRcT rc;
|
|
uint8_t value;
|
|
|
|
DevAssert(msg != NULL);
|
|
DevAssert(bearer_flags != NULL);
|
|
|
|
value = (bearer_flags->vb << 1) | bearer_flags->ppc;
|
|
|
|
rc = nwGtpv2cMsgAddIe(*msg, NW_GTPV2C_IE_BEARER_FLAGS, 1, 0, (NwU8T*)&value);
|
|
DevAssert(NW_OK == rc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
NwRcT s11_indication_flags_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
indication_flags_t *indication_flags = (indication_flags_t*)arg;
|
|
|
|
DevAssert(indication_flags != NULL);
|
|
|
|
if (ieLength < 3) {
|
|
return NW_GTPV2C_IE_INCORRECT;
|
|
}
|
|
|
|
*indication_flags = ieValue[0] | (ieValue[1] << 8) | (ieValue[2] << 16);
|
|
|
|
S11_DEBUG("\t- Indication Flags %06x\n", *indication_flags);
|
|
|
|
return NW_OK;
|
|
}
|
|
|
|
NwRcT s11_fqcsid_ie_get(
|
|
NwU8T ieType, NwU8T ieLength, NwU8T ieInstance, NwU8T *ieValue, void *arg)
|
|
{
|
|
FQ_CSID_t *fq_csid = (FQ_CSID_t *)arg;
|
|
|
|
DevAssert(fq_csid != NULL);
|
|
|
|
fq_csid->node_id_type = (ieValue[0] & 0xF0) >> 4;
|
|
S11_DEBUG("\t- FQ-CSID type %u\n", fq_csid->node_id_type);
|
|
|
|
/* NOTE: Values of Number of CSID other than 1 are only employed in the
|
|
* Delete PDN Connection Set Request and Response.
|
|
*/
|
|
if ((ieValue[0] & 0x0F) != 1) {
|
|
return NW_GTPV2C_IE_INCORRECT;
|
|
}
|
|
|
|
switch (fq_csid->node_id_type) {
|
|
case GLOBAL_UNICAST_IPv4: {
|
|
if (ieLength != 7) {
|
|
return NW_GTPV2C_IE_INCORRECT;
|
|
}
|
|
|
|
fq_csid->node_id.unicast_ipv4 = (ieValue[1] << 24) |
|
|
(ieValue[2] << 16) | (ieValue[3] << 8) | (ieValue[4]);
|
|
fq_csid->csid = (ieValue[5] << 8) | ieValue[6];
|
|
|
|
S11_DEBUG("\t- v4 address ["IPV4_ADDR"]\n",
|
|
IPV4_ADDR_FORMAT(fq_csid->node_id.unicast_ipv4));
|
|
}
|
|
break;
|
|
|
|
case GLOBAL_UNICAST_IPv6: {
|
|
char ipv6[40];
|
|
|
|
if (ieLength != 19) {
|
|
return NW_GTPV2C_IE_INCORRECT;
|
|
}
|
|
|
|
memcpy(fq_csid->node_id.unicast_ipv6, &ieValue[1], 16);
|
|
fq_csid->csid = (ieValue[17] << 8) | ieValue[18];
|
|
|
|
/* Convert the ipv6 to printable string */
|
|
inet_ntop(AF_INET6, fq_csid->node_id.unicast_ipv6, ipv6, 40);
|
|
|
|
S11_DEBUG("\t- v6 address [%s]\n", fq_csid->node_id.unicast_ipv6);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return NW_GTPV2C_IE_INCORRECT;
|
|
}
|
|
|
|
S11_DEBUG("\t- CSID 0x%04x\n", fq_csid->csid);
|
|
return NW_OK;
|
|
}
|