393 lines
14 KiB
C
393 lines
14 KiB
C
/*******************************************************************************
|
|
|
|
Eurecom OpenAirInterface
|
|
Copyright(c) 1999 - 2013 Eurecom
|
|
|
|
This program is free software; you can redistribute it and/or modify it
|
|
under the terms and conditions of the GNU General Public License,
|
|
version 2, as published by the Free Software Foundation.
|
|
|
|
This program is distributed in the hope 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
|
|
this program; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
The full GNU General Public License is included in this distribution in
|
|
the file called "COPYING".
|
|
|
|
Contact Information
|
|
Openair Admin: openair_admin@eurecom.fr
|
|
Openair Tech : openair_tech@eurecom.fr
|
|
Forums : http://forums.eurecom.fr/openairinterface
|
|
Address : EURECOM, Campus SophiaTech, 450 Route des Chappes
|
|
06410 Biot FRANCE
|
|
|
|
*******************************************************************************/
|
|
|
|
#include <stdint.h>
|
|
|
|
#include "common_types.h"
|
|
#include "s6a_defs.h"
|
|
|
|
#include "assertions.h"
|
|
|
|
static inline
|
|
int s6a_parse_subscriber_status(struct avp_hdr *hdr_sub_status,
|
|
subscriber_status_t *sub_status)
|
|
{
|
|
DevCheck(hdr_sub_status->avp_value->u32 < SS_MAX,
|
|
hdr_sub_status->avp_value->u32, SS_MAX, 0);
|
|
|
|
*sub_status = hdr_sub_status->avp_value->u32;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline
|
|
int s6a_parse_msisdn(struct avp_hdr *hdr_msisdn, char *msisdn, int *length)
|
|
{
|
|
DevCheck(hdr_msisdn->avp_value->os.len <= MSISDN_LENGTH,
|
|
hdr_msisdn->avp_value->os.len, MSISDN_LENGTH, 0);
|
|
if (hdr_msisdn->avp_value->os.len == 0)
|
|
return 0;
|
|
*length = sprintf(msisdn, "%*s", (int)hdr_msisdn->avp_value->os.len,
|
|
hdr_msisdn->avp_value->os.data);
|
|
return 0;
|
|
}
|
|
|
|
static inline
|
|
int s6a_parse_network_access_mode(struct avp_hdr *hdr_network_am,
|
|
network_access_mode_t *access_mode)
|
|
{
|
|
DevCheck(hdr_network_am->avp_value->u32 < NAM_MAX &&
|
|
hdr_network_am->avp_value->u32 != NAM_RESERVED,
|
|
hdr_network_am->avp_value->u32, NAM_MAX, NAM_RESERVED);
|
|
|
|
*access_mode = hdr_network_am->avp_value->u32;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline
|
|
int s6a_parse_access_restriction_data(struct avp_hdr *hdr_access_restriction,
|
|
access_restriction_t *access_restriction)
|
|
{
|
|
DevCheck(hdr_access_restriction->avp_value->u32 < ARD_MAX,
|
|
hdr_access_restriction->avp_value->u32, ARD_MAX, 0);
|
|
*access_restriction = hdr_access_restriction->avp_value->u32;
|
|
return 0;
|
|
}
|
|
|
|
static inline
|
|
int s6a_parse_bitrate(struct avp_hdr *hdr_bitrate, bitrate_t *bitrate)
|
|
{
|
|
*bitrate = hdr_bitrate->avp_value->u32;
|
|
return 0;
|
|
}
|
|
|
|
static inline
|
|
int s6a_parse_ambr(struct avp *avp_ambr, ambr_t *ambr)
|
|
{
|
|
struct avp *avp = NULL;
|
|
struct avp_hdr *hdr;
|
|
|
|
CHECK_FCT(fd_msg_browse(avp_ambr, MSG_BRW_FIRST_CHILD, &avp, NULL));
|
|
if (!avp) {
|
|
/* Child avps for ambr are mandatory */
|
|
return -1;
|
|
}
|
|
while(avp) {
|
|
CHECK_FCT(fd_msg_avp_hdr(avp, &hdr));
|
|
switch(hdr->avp_code) {
|
|
case AVP_CODE_BANDWIDTH_UL:
|
|
CHECK_FCT(s6a_parse_bitrate(hdr, &ambr->br_ul));
|
|
break;
|
|
case AVP_CODE_BANDWIDTH_DL:
|
|
CHECK_FCT(s6a_parse_bitrate(hdr, &ambr->br_dl));
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
/* Go to next AVP in the grouped AVP */
|
|
CHECK_FCT(fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static inline
|
|
int s6a_parse_all_apn_conf_inc_ind(struct avp_hdr *hdr, all_apn_conf_ind_t *ptr)
|
|
{
|
|
DevCheck(hdr->avp_value->u32 < ALL_APN_MAX,
|
|
hdr->avp_value->u32, ALL_APN_MAX, 0);
|
|
*ptr = hdr->avp_value->u32;
|
|
return 0;
|
|
}
|
|
|
|
static inline
|
|
int s6a_parse_pdn_type(struct avp_hdr *hdr, pdn_type_t *pdn_type)
|
|
{
|
|
DevCheck(hdr->avp_value->u32 < IP_MAX,
|
|
hdr->avp_value->u32, IP_MAX, 0);
|
|
*pdn_type = hdr->avp_value->u32;
|
|
return 0;
|
|
}
|
|
|
|
static inline
|
|
int s6a_parse_service_selection(struct avp_hdr *hdr_service_selection,
|
|
char *service_selection, int *length)
|
|
{
|
|
DevCheck(hdr_service_selection->avp_value->os.len <= APN_MAX_LENGTH,
|
|
hdr_service_selection->avp_value->os.len, APN_MAX_LENGTH, 0);
|
|
*length = sprintf(service_selection, "%*s",
|
|
(int)hdr_service_selection->avp_value->os.len,
|
|
hdr_service_selection->avp_value->os.data);
|
|
return 0;
|
|
}
|
|
|
|
static inline
|
|
int s6a_parse_qci(struct avp_hdr *hdr, qci_t *qci)
|
|
{
|
|
DevCheck(hdr->avp_value->u32 < QCI_MAX,
|
|
hdr->avp_value->u32, QCI_MAX, 0);
|
|
*qci = hdr->avp_value->u32;
|
|
return 0;
|
|
}
|
|
|
|
static inline
|
|
int s6a_parse_priority_level(struct avp_hdr *hdr, priority_level_t *priority_level)
|
|
{
|
|
DevCheck(hdr->avp_value->u32 <= PRIORITY_LEVEL_MAX &&
|
|
hdr->avp_value->u32 >= PRIORITY_LEVEL_MIN, hdr->avp_value->u32,
|
|
PRIORITY_LEVEL_MAX, PRIORITY_LEVEL_MIN);
|
|
*priority_level = (priority_level_t)hdr->avp_value->u32;
|
|
return 0;
|
|
}
|
|
|
|
static inline
|
|
int s6a_parse_pre_emp_capability(struct avp_hdr *hdr, pre_emp_capability_t *pre_emp_capability)
|
|
{
|
|
DevCheck(hdr->avp_value->u32 < PRE_EMPTION_CAPABILITY_MAX, hdr->avp_value->u32,
|
|
PRE_EMPTION_CAPABILITY_MAX, 0);
|
|
*pre_emp_capability = hdr->avp_value->u32;
|
|
return 0;
|
|
}
|
|
|
|
static inline
|
|
int s6a_parse_pre_emp_vulnerability(struct avp_hdr *hdr, pre_emp_vulnerability_t *pre_emp_vulnerability)
|
|
{
|
|
DevCheck(hdr->avp_value->u32 < PRE_EMPTION_VULNERABILITY_MAX, hdr->avp_value->u32,
|
|
PRE_EMPTION_VULNERABILITY_MAX, 0);
|
|
*pre_emp_vulnerability = hdr->avp_value->u32;
|
|
return 0;
|
|
}
|
|
|
|
static inline
|
|
int s6a_parse_allocation_retention_priority(struct avp *avp_arp,
|
|
allocation_retention_priority_t *ptr)
|
|
{
|
|
struct avp *avp = NULL;
|
|
struct avp_hdr *hdr;
|
|
|
|
/* If the Pre-emption-Capability AVP is not present in the
|
|
* Allocation-Retention-Priority AVP, the default value shall be
|
|
* PRE-EMPTION_CAPABILITY_DISABLED (1).
|
|
*/
|
|
ptr->pre_emp_capability = PRE_EMPTION_CAPABILITY_DISABLED;
|
|
|
|
/* If the Pre-emption-Vulnerability AVP is not present in the
|
|
* Allocation-Retention-Priority AVP, the default value shall be
|
|
* PRE-EMPTION_VULNERABILITY_ENABLED (0).
|
|
*/
|
|
ptr->pre_emp_vulnerability = PRE_EMPTION_VULNERABILITY_ENABLED;
|
|
|
|
CHECK_FCT(fd_msg_browse(avp_arp, MSG_BRW_FIRST_CHILD, &avp, NULL));
|
|
while(avp) {
|
|
CHECK_FCT(fd_msg_avp_hdr(avp, &hdr));
|
|
switch(hdr->avp_code) {
|
|
case AVP_CODE_PRIORITY_LEVEL:
|
|
CHECK_FCT(s6a_parse_priority_level(hdr, &ptr->priority_level));
|
|
break;
|
|
case AVP_CODE_PRE_EMPTION_CAPABILITY:
|
|
CHECK_FCT(s6a_parse_pre_emp_capability(hdr, &ptr->pre_emp_capability));
|
|
break;
|
|
case AVP_CODE_PRE_EMPTION_VULNERABILITY:
|
|
CHECK_FCT(s6a_parse_pre_emp_vulnerability(hdr, &ptr->pre_emp_vulnerability));
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
/* Go to next AVP in the grouped AVP */
|
|
CHECK_FCT(fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static inline
|
|
int s6a_parse_eps_subscribed_qos_profile(struct avp *avp_qos,
|
|
eps_subscribed_qos_profile_t *ptr)
|
|
{
|
|
struct avp *avp = NULL;
|
|
struct avp_hdr *hdr;
|
|
|
|
CHECK_FCT(fd_msg_browse(avp_qos, MSG_BRW_FIRST_CHILD, &avp, NULL));
|
|
while(avp) {
|
|
CHECK_FCT(fd_msg_avp_hdr(avp, &hdr));
|
|
switch(hdr->avp_code) {
|
|
case AVP_CODE_QCI:
|
|
CHECK_FCT(s6a_parse_qci(hdr, &ptr->qci));
|
|
break;
|
|
case AVP_CODE_ALLOCATION_RETENTION_PRIORITY:
|
|
CHECK_FCT(s6a_parse_allocation_retention_priority(avp, &ptr->allocation_retention_priority));
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
/* Go to next AVP in the grouped AVP */
|
|
CHECK_FCT(fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static inline
|
|
int s6a_parse_ip_address(struct avp_hdr *hdr, ip_address_t *ip_address)
|
|
{
|
|
uint16_t ip_type;
|
|
|
|
DevCheck(hdr->avp_value->os.len >= 2, hdr->avp_value->os.len, 2, 0);
|
|
|
|
ip_type = (hdr->avp_value->os.data[0] << 8) | (hdr->avp_value->os.data[1]);
|
|
|
|
if (ip_type == IANA_IPV4) {
|
|
/* This is an IPv4 address */
|
|
ip_address->pdn_type = IPv4;
|
|
DevCheck(hdr->avp_value->os.len == 6, hdr->avp_value->os.len, 6, ip_type);
|
|
memcpy(ip_address->address.ipv4_address, &hdr->avp_value->os.data[2], 4);
|
|
} else if (ip_type == IANA_IPV6) {
|
|
/* This is an IPv6 address */
|
|
ip_address->pdn_type = IPv6;
|
|
DevCheck(hdr->avp_value->os.len == 18, hdr->avp_value->os.len, 18, ip_type);
|
|
memcpy(ip_address->address.ipv6_address, &hdr->avp_value->os.data[2], 16);
|
|
} else {
|
|
/* unhandled case... */
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static inline
|
|
int s6a_parse_apn_configuration(struct avp *avp_apn_conf_prof, apn_configuration_t *apn_config)
|
|
{
|
|
struct avp *avp = NULL;
|
|
struct avp_hdr *hdr;
|
|
|
|
CHECK_FCT(fd_msg_browse(avp_apn_conf_prof, MSG_BRW_FIRST_CHILD, &avp, NULL));
|
|
while(avp) {
|
|
CHECK_FCT(fd_msg_avp_hdr(avp, &hdr));
|
|
switch(hdr->avp_code) {
|
|
case AVP_CODE_CONTEXT_IDENTIFIER:
|
|
apn_config->context_identifier = hdr->avp_value->u32;
|
|
break;
|
|
case AVP_CODE_SERVED_PARTY_IP_ADDRESS:
|
|
if (apn_config->nb_ip_address == 2) {
|
|
DevMessage("Only two IP addresses can be provided");
|
|
}
|
|
CHECK_FCT(s6a_parse_ip_address(hdr, &apn_config->ip_address[apn_config->nb_ip_address]));
|
|
apn_config->nb_ip_address++;
|
|
break;
|
|
case AVP_CODE_PDN_TYPE:
|
|
CHECK_FCT(s6a_parse_pdn_type(hdr, &apn_config->pdn_type));
|
|
break;
|
|
case AVP_CODE_SERVICE_SELECTION:
|
|
CHECK_FCT(s6a_parse_service_selection(hdr, apn_config->service_selection,
|
|
&apn_config->service_selection_length));
|
|
break;
|
|
case AVP_CODE_EPS_SUBSCRIBED_QOS_PROFILE:
|
|
CHECK_FCT(s6a_parse_eps_subscribed_qos_profile(avp, &apn_config->subscribed_qos));
|
|
break;
|
|
case AVP_CODE_AMBR:
|
|
CHECK_FCT(s6a_parse_ambr(avp, &apn_config->ambr));
|
|
break;
|
|
}
|
|
/* Go to next AVP in the grouped AVP */
|
|
CHECK_FCT(fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static inline
|
|
int s6a_parse_apn_configuration_profile(struct avp *avp_apn_conf_prof,
|
|
apn_config_profile_t *apn_config_profile)
|
|
{
|
|
struct avp *avp = NULL;
|
|
struct avp_hdr *hdr;
|
|
|
|
CHECK_FCT(fd_msg_browse(avp_apn_conf_prof, MSG_BRW_FIRST_CHILD, &avp, NULL));
|
|
while(avp) {
|
|
CHECK_FCT(fd_msg_avp_hdr(avp, &hdr));
|
|
switch(hdr->avp_code) {
|
|
case AVP_CODE_CONTEXT_IDENTIFIER:
|
|
apn_config_profile->context_identifier = hdr->avp_value->u32;
|
|
break;
|
|
case AVP_CODE_ALL_APN_CONFIG_INC_IND:
|
|
CHECK_FCT(s6a_parse_all_apn_conf_inc_ind(hdr, &apn_config_profile->all_apn_conf_ind));
|
|
break;
|
|
case AVP_CODE_APN_CONFIGURATION: {
|
|
DevCheck(apn_config_profile->nb_apns < MAX_APN_PER_UE,
|
|
apn_config_profile->nb_apns, MAX_APN_PER_UE, 0);
|
|
CHECK_FCT(s6a_parse_apn_configuration(
|
|
avp, &apn_config_profile->apn_configuration[apn_config_profile->nb_apns]));
|
|
apn_config_profile->nb_apns++;
|
|
} break;
|
|
}
|
|
/* Go to next AVP in the grouped AVP */
|
|
CHECK_FCT(fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int s6a_parse_subscription_data(struct avp *avp_subscription_data,
|
|
subscription_data_t *subscription_data)
|
|
{
|
|
struct avp *avp = NULL;
|
|
struct avp_hdr *hdr;
|
|
|
|
CHECK_FCT(fd_msg_browse(avp_subscription_data, MSG_BRW_FIRST_CHILD, &avp, NULL));
|
|
while(avp) {
|
|
CHECK_FCT(fd_msg_avp_hdr(avp, &hdr));
|
|
|
|
switch(hdr->avp_code) {
|
|
case AVP_CODE_SUBSCRIBER_STATUS:
|
|
CHECK_FCT(s6a_parse_subscriber_status(hdr, &subscription_data->subscriber_status));
|
|
break;
|
|
case AVP_CODE_MSISDN:
|
|
CHECK_FCT(s6a_parse_msisdn(hdr, subscription_data->msisdn,
|
|
&subscription_data->msisdn_length));
|
|
break;
|
|
case AVP_CODE_NETWORK_ACCESS_MODE:
|
|
CHECK_FCT(s6a_parse_network_access_mode(hdr, &subscription_data->access_mode));
|
|
break;
|
|
case AVP_CODE_ACCESS_RESTRICTION_DATA:
|
|
CHECK_FCT(s6a_parse_access_restriction_data(hdr, &subscription_data->access_restriction));
|
|
break;
|
|
case AVP_CODE_AMBR:
|
|
CHECK_FCT(s6a_parse_ambr(avp, &subscription_data->subscribed_ambr));
|
|
break;
|
|
case AVP_CODE_APN_CONFIGURATION_PROFILE:
|
|
CHECK_FCT(s6a_parse_apn_configuration_profile(avp, &subscription_data->apn_config_profile));
|
|
break;
|
|
case AVP_CODE_SUBSCRIBED_PERIODIC_RAU_TAU_TIMER:
|
|
subscription_data->rau_tau_timer = hdr->avp_value->u32;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
/* Go to next AVP in the grouped AVP */
|
|
CHECK_FCT(fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL));
|
|
}
|
|
return 0;
|
|
}
|