import gprs_gsup_message.[ch] from openbsc as gsup.[ch]

Move those routines from OpenBSC to libosmogsm, so they can be
re-used from other programs.  I think it was a mistake to add them only
inside the openbsc repository in the first place.  We need to pay more
attention to this in the future.
This commit is contained in:
Harald Welte 2016-04-25 18:46:22 +02:00
parent 9709b2e6fd
commit 3b6fb0880c
10 changed files with 908 additions and 4 deletions

View File

@ -70,6 +70,7 @@ nobase_include_HEADERS = \
osmocom/gsm/gsm48.h \
osmocom/gsm/gsm48_ie.h \
osmocom/gsm/gsm_utils.h \
osmocom/gsm/gsup.h \
osmocom/gsm/ipa.h \
osmocom/gsm/lapd_core.h \
osmocom/gsm/lapdm.h \

View File

@ -91,7 +91,8 @@ void logp(int subsys, const char *file, int line, int cont, const char *format,
#define DLCTRL -8 /*!< Control Interface */
#define DLGTP -9 /*!< GTP (GPRS Tunneling Protocol */
#define DLSTATS -10 /*!< Statistics */
#define OSMO_NUM_DLIB 10 /*!< Number of logging sub-systems in libraries */
#define DLGSUP 11 /*!< Generic Subscriber Update Protocol */
#define OSMO_NUM_DLIB 11 /*!< Number of logging sub-systems in libraries */
/*! Configuration of singgle log category / sub-system */
struct log_category {

139
include/osmocom/gsm/gsup.h Normal file
View File

@ -0,0 +1,139 @@
/* Osmocom Subscriber Update Protocol message encoder/decoder */
/* (C) 2014 by Sysmocom s.f.m.c. GmbH, Author: Jacob Erlbeck
* (C) 2016 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <stdint.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/crypt/auth.h>
/*! Maximum nubmer of PDP inside \ref osmo_gsup_message */
#define OSMO_GSUP_MAX_NUM_PDP_INFO 10 /* GSM 09.02 limits this to 50 */
/*! Maximum number of auth info inside \ref osmo_gsup_message */
#define OSMO_GSUP_MAX_NUM_AUTH_INFO 5
/*! Maximum number of octets encoding MSISDN in BCD format */
#define OSMO_GSUP_MAX_MSISDN_LEN 9
#define OSMO_GSUP_PDP_TYPE_SIZE 2
/*! Information Element Identifiers for GSUP IEs */
enum osmo_gsup_iei {
OSMO_GSUP_IMSI_IE = 0x01,
OSMO_GSUP_CAUSE_IE = 0x02,
OSMO_GSUP_AUTH_TUPLE_IE = 0x03,
OSMO_GSUP_PDP_INFO_COMPL_IE = 0x04,
OSMO_GSUP_PDP_INFO_IE = 0x05,
OSMO_GSUP_CANCEL_TYPE_IE = 0x06,
OSMO_GSUP_FREEZE_PTMSI_IE = 0x07,
OSMO_GSUP_MSISDN_IE = 0x08,
OSMO_GSUP_HLR_NUMBER_IE = 0x09,
OSMO_GSUP_PDP_CONTEXT_ID_IE = 0x10,
OSMO_GSUP_PDP_TYPE_IE = 0x11,
OSMO_GSUP_ACCESS_POINT_NAME_IE = 0x12,
OSMO_GSUP_PDP_QOS_IE = 0x13,
OSMO_GSUP_RAND_IE = 0x20,
OSMO_GSUP_SRES_IE = 0x21,
OSMO_GSUP_KC_IE = 0x22,
/* 3G support */
OSMO_GSUP_IK_IE = 0x23,
OSMO_GSUP_CK_IE = 0x24,
OSMO_GSUP_AUTN_IE = 0x25,
OSMO_GSUP_AUTS_IE = 0x26,
OSMO_GSUP_RES_IE = 0x27,
};
/*! GSUP message type */
enum osmo_gsup_message_type {
OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST = 0b00000100,
OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR = 0b00000101,
OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT = 0b00000110,
OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST = 0b00001000,
OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR = 0b00001001,
OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT = 0b00001010,
OSMO_GSUP_MSGT_PURGE_MS_REQUEST = 0b00001100,
OSMO_GSUP_MSGT_PURGE_MS_ERROR = 0b00001101,
OSMO_GSUP_MSGT_PURGE_MS_RESULT = 0b00001110,
OSMO_GSUP_MSGT_INSERT_DATA_REQUEST = 0b00010000,
OSMO_GSUP_MSGT_INSERT_DATA_ERROR = 0b00010001,
OSMO_GSUP_MSGT_INSERT_DATA_RESULT = 0b00010010,
OSMO_GSUP_MSGT_DELETE_DATA_REQUEST = 0b00010100,
OSMO_GSUP_MSGT_DELETE_DATA_ERROR = 0b00010101,
OSMO_GSUP_MSGT_DELETE_DATA_RESULT = 0b00010110,
OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST = 0b00011100,
OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR = 0b00011101,
OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT = 0b00011110,
};
#define OSMO_GSUP_IS_MSGT_REQUEST(msgt) (((msgt) & 0b00000011) == 0b00)
#define OSMO_GSUP_IS_MSGT_ERROR(msgt) (((msgt) & 0b00000011) == 0b01)
#define OSMO_GSUP_TO_MSGT_ERROR(msgt) (((msgt) & 0b11111100) | 0b01)
enum osmo_gsup_cancel_type {
OSMO_GSUP_CANCEL_TYPE_UPDATE = 1, /* on wire: 0 */
OSMO_GSUP_CANCEL_TYPE_WITHDRAW = 2, /* on wire: 1 */
};
/*! parsed/decoded PDP context information */
struct osmo_gsup_pdp_info {
unsigned int context_id;
int have_info;
/*! Type of PDP context */
uint16_t pdp_type;
/*! APN information, still in encoded form. Can be NULL if no
* APN information included */
const uint8_t *apn_enc;
/*! length (in octets) of apn_enc */
size_t apn_enc_len;
/*! QoS information, still in encoded form. Can be NULL if no
* QoS information included */
const uint8_t *qos_enc;
/*! length (in octets) of qos_enc */
size_t qos_enc_len;
};
/*! parsed/decoded GSUP protocol message */
struct osmo_gsup_message {
enum osmo_gsup_message_type message_type;
char imsi[GSM23003_IMSI_MAX_DIGITS+2];
enum gsm48_gmm_cause cause;
enum osmo_gsup_cancel_type cancel_type;
int pdp_info_compl;
int freeze_ptmsi;
struct osmo_auth_vector auth_vectors[OSMO_GSUP_MAX_NUM_AUTH_INFO];
size_t num_auth_vectors;
struct osmo_gsup_pdp_info pdp_infos[OSMO_GSUP_MAX_NUM_PDP_INFO];
size_t num_pdp_infos;
const uint8_t *msisdn_enc;
size_t msisdn_enc_len;
const uint8_t *hlr_enc;
size_t hlr_enc_len;
const uint8_t *auts;
};
int osmo_gsup_decode(const uint8_t *data, size_t data_len,
struct osmo_gsup_message *gsup_msg);
void osmo_gsup_encode(struct msgb *msg, const struct osmo_gsup_message *gsup_msg);

View File

@ -26,7 +26,8 @@ libgsmint_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c comp128v23.c \
auth_core.c auth_comp128v1.c auth_comp128v23.c \
auth_milenage.c milenage/aes-encblock.c \
milenage/aes-internal.c milenage/aes-internal-enc.c \
milenage/milenage.c gan.c ipa.c gsm0341.c apn.c
milenage/milenage.c gan.c ipa.c gsm0341.c apn.c \
gsup.c
libgsmint_la_LDFLAGS = -no-undefined
libgsmint_la_LIBADD = ../libosmocore.la

472
src/gsm/gsup.c Normal file
View File

@ -0,0 +1,472 @@
/* Osmocom Subscriber Update Protocol message encoder/decoder */
/*
* (C) 2014 by Sysmocom s.f.m.c. GmbH
* (C) 2015 by Holger Hans Peter Freyther
* (C) 2016 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* Author: Jacob Erlbeck
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/gsm/tlv.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/logging.h>
#include <osmocom/gsm/gsm48_ie.h>
#include <osmocom/gsm/gsup.h>
#include <stdint.h>
static int decode_pdp_info(uint8_t *data, size_t data_len,
struct osmo_gsup_pdp_info *pdp_info)
{
int rc;
uint8_t tag;
uint8_t *value;
size_t value_len;
/* specific parts */
while (data_len > 0) {
enum osmo_gsup_iei iei;
rc = osmo_shift_tlv(&data, &data_len, &tag, &value, &value_len);
if (rc < 0)
return -GMM_CAUSE_PROTO_ERR_UNSPEC;
iei = tag;
switch (iei) {
case OSMO_GSUP_PDP_CONTEXT_ID_IE:
pdp_info->context_id = osmo_decode_big_endian(value, value_len);
break;
case OSMO_GSUP_PDP_TYPE_IE:
pdp_info->pdp_type =
osmo_decode_big_endian(value, value_len) & 0x0fff;
break;
case OSMO_GSUP_ACCESS_POINT_NAME_IE:
pdp_info->apn_enc = value;
pdp_info->apn_enc_len = value_len;
break;
case OSMO_GSUP_PDP_QOS_IE:
pdp_info->qos_enc = value;
pdp_info->qos_enc_len = value_len;
break;
default:
LOGP(DLGSUP, LOGL_ERROR,
"GSUP IE type %d not expected in PDP info\n", iei);
continue;
}
}
return 0;
}
static int decode_auth_info(uint8_t *data, size_t data_len,
struct osmo_auth_vector *auth_vector)
{
int rc;
uint8_t tag;
uint8_t *value;
size_t value_len;
enum osmo_gsup_iei iei;
uint8_t presence = 0;
/* specific parts */
while (data_len > 0) {
rc = osmo_shift_tlv(&data, &data_len, &tag, &value, &value_len);
if (rc < 0)
return -GMM_CAUSE_PROTO_ERR_UNSPEC;
iei = tag;
switch (iei) {
case OSMO_GSUP_RAND_IE:
if (value_len != sizeof(auth_vector->rand))
goto parse_error;
memcpy(auth_vector->rand, value, value_len);
presence |= (1 << 0);
break;
case OSMO_GSUP_SRES_IE:
if (value_len != sizeof(auth_vector->sres))
goto parse_error;
memcpy(auth_vector->sres, value, value_len);
presence |= (1 << 1);
break;
case OSMO_GSUP_KC_IE:
if (value_len != sizeof(auth_vector->kc))
goto parse_error;
memcpy(auth_vector->kc, value, value_len);
presence |= (1 << 2);
break;
case OSMO_GSUP_IK_IE:
if (value_len != sizeof(auth_vector->ik))
goto parse_error;
memcpy(auth_vector->ik, value, value_len);
presence |= (1 << 4);
break;
case OSMO_GSUP_CK_IE:
if (value_len != sizeof(auth_vector->ck))
goto parse_error;
memcpy(auth_vector->ck, value, value_len);
presence |= (1 << 5);
break;
case OSMO_GSUP_AUTN_IE:
if (value_len != sizeof(auth_vector->autn))
goto parse_error;
memcpy(auth_vector->autn, value, value_len);
presence |= (1 << 6);
break;
case OSMO_GSUP_RES_IE:
if (value_len > sizeof(auth_vector->res))
goto parse_error;
memcpy(auth_vector->res, value, value_len);
auth_vector->res_len = value_len;
presence |= (1 << 7);
break;
default:
LOGP(DLGSUP, LOGL_ERROR,
"GSUP IE type %d not expected in PDP info\n", iei);
continue;
}
}
if (presence & 0x07)
auth_vector->auth_types |= OSMO_AUTH_TYPE_GSM;
if (presence & 0xf0)
auth_vector->auth_types |= OSMO_AUTH_TYPE_UMTS;
return 0;
parse_error:
LOGP(DLGSUP, LOGL_ERROR,
"GSUP IE type %d, length %zu invalid in PDP info\n", iei, value_len);
return -1;
}
/*! Decode (parse) a GSUP message
* \param[in] const_data input data to be parsed
* \param[in] data_len length of input (\a const_data)
* \param[out] gsup_msg callee-allocated output data structure
* \returns 0 on success; negative otherwise
*/
int osmo_gsup_decode(const uint8_t *const_data, size_t data_len,
struct osmo_gsup_message *gsup_msg)
{
int rc;
uint8_t tag;
/* the shift/match functions expect non-const pointers, but we'll
* either copy the data or cast pointers back to const before returning
* them
*/
uint8_t *data = (uint8_t *)const_data;
uint8_t *value;
size_t value_len;
static const struct osmo_gsup_pdp_info empty_pdp_info = {0};
static const struct osmo_auth_vector empty_auth_info = {0};
static const struct osmo_gsup_message empty_gsup_message = {0};
*gsup_msg = empty_gsup_message;
/* generic part */
rc = osmo_shift_v_fixed(&data, &data_len, 1, &value);
if (rc < 0)
return -GMM_CAUSE_INV_MAND_INFO;
gsup_msg->message_type = osmo_decode_big_endian(value, 1);
rc = osmo_match_shift_tlv(&data, &data_len, OSMO_GSUP_IMSI_IE,
&value, &value_len);
if (rc <= 0)
return -GMM_CAUSE_INV_MAND_INFO;
if (value_len * 2 + 1 > sizeof(gsup_msg->imsi))
return -GMM_CAUSE_INV_MAND_INFO;
/* Note that gsm48_decode_bcd_number expects the number of encoded IMSI
* octets in the first octet. By coincidence (the TLV encoding) the byte
* before the value part already contains this length so we can use it
* here.
*/
OSMO_ASSERT(value[-1] == value_len);
gsm48_decode_bcd_number(gsup_msg->imsi, sizeof(gsup_msg->imsi),
value - 1, 0);
/* specific parts */
while (data_len > 0) {
enum osmo_gsup_iei iei;
struct osmo_gsup_pdp_info pdp_info;
struct osmo_auth_vector auth_info;
rc = osmo_shift_tlv(&data, &data_len, &tag, &value, &value_len);
if (rc < 0)
return -GMM_CAUSE_PROTO_ERR_UNSPEC;
iei = tag;
switch (iei) {
case OSMO_GSUP_IMSI_IE:
case OSMO_GSUP_PDP_TYPE_IE:
case OSMO_GSUP_ACCESS_POINT_NAME_IE:
case OSMO_GSUP_RAND_IE:
case OSMO_GSUP_SRES_IE:
case OSMO_GSUP_KC_IE:
LOGP(DLGSUP, LOGL_NOTICE,
"GSUP IE type %d not expected (ignored)\n", iei);
continue;
case OSMO_GSUP_CAUSE_IE:
gsup_msg->cause = osmo_decode_big_endian(value, value_len);
break;
case OSMO_GSUP_CANCEL_TYPE_IE:
gsup_msg->cancel_type =
osmo_decode_big_endian(value, value_len) + 1;
break;
case OSMO_GSUP_PDP_INFO_COMPL_IE:
gsup_msg->pdp_info_compl = 1;
break;
case OSMO_GSUP_FREEZE_PTMSI_IE:
gsup_msg->freeze_ptmsi = 1;
break;
case OSMO_GSUP_PDP_CONTEXT_ID_IE:
/* When these IE appear in the top-level part of the
* message, they are used by Delete Subscr Info to delete
* single entries. We don't have an extra list for
* these but use the PDP info list instead */
/* fall through */
case OSMO_GSUP_PDP_INFO_IE:
if (gsup_msg->num_pdp_infos >= OSMO_GSUP_MAX_NUM_PDP_INFO) {
LOGP(DLGSUP, LOGL_ERROR,
"GSUP IE type %d (PDP_INFO) max exceeded\n",
iei);
return -GMM_CAUSE_COND_IE_ERR;
}
pdp_info = empty_pdp_info;
if (iei == OSMO_GSUP_PDP_INFO_IE) {
rc = decode_pdp_info(value, value_len, &pdp_info);
if (rc < 0)
return rc;
pdp_info.have_info = 1;
} else {
pdp_info.context_id =
osmo_decode_big_endian(value, value_len);
}
gsup_msg->pdp_infos[gsup_msg->num_pdp_infos++] =
pdp_info;
break;
case OSMO_GSUP_AUTH_TUPLE_IE:
if (gsup_msg->num_auth_vectors >= OSMO_GSUP_MAX_NUM_AUTH_INFO) {
LOGP(DLGSUP, LOGL_ERROR,
"GSUP IE type %d (AUTH_INFO) max exceeded\n",
iei);
return -GMM_CAUSE_INV_MAND_INFO;
}
auth_info = empty_auth_info;
rc = decode_auth_info(value, value_len, &auth_info);
if (rc < 0)
return rc;
gsup_msg->auth_vectors[gsup_msg->num_auth_vectors++] =
auth_info;
break;
case OSMO_GSUP_AUTS_IE:
if (value_len != 16) {
LOGP(DLGSUP, LOGL_ERROR,
"AUTS length != 16 received\n");
return -GMM_CAUSE_COND_IE_ERR;
}
gsup_msg->auts = value;
break;
case OSMO_GSUP_MSISDN_IE:
gsup_msg->msisdn_enc = value;
gsup_msg->msisdn_enc_len = value_len;
break;
case OSMO_GSUP_HLR_NUMBER_IE:
gsup_msg->hlr_enc = value;
gsup_msg->hlr_enc_len = value_len;
break;
default:
LOGP(DLGSUP, LOGL_NOTICE,
"GSUP IE type %d unknown\n", iei);
continue;
}
}
return 0;
}
static void encode_pdp_info(struct msgb *msg, enum osmo_gsup_iei iei,
const struct osmo_gsup_pdp_info *pdp_info)
{
uint8_t *len_field;
size_t old_len;
uint8_t u8;
len_field = msgb_tlv_put(msg, iei, 0, NULL) - 1;
old_len = msgb_length(msg);
u8 = pdp_info->context_id;
msgb_tlv_put(msg, OSMO_GSUP_PDP_CONTEXT_ID_IE, sizeof(u8), &u8);
if (pdp_info->pdp_type) {
msgb_tlv_put(msg, OSMO_GSUP_PDP_TYPE_IE,
OSMO_GSUP_PDP_TYPE_SIZE,
osmo_encode_big_endian(pdp_info->pdp_type | 0xf000,
OSMO_GSUP_PDP_TYPE_SIZE));
}
if (pdp_info->apn_enc) {
msgb_tlv_put(msg, OSMO_GSUP_ACCESS_POINT_NAME_IE,
pdp_info->apn_enc_len, pdp_info->apn_enc);
}
if (pdp_info->qos_enc) {
msgb_tlv_put(msg, OSMO_GSUP_PDP_QOS_IE,
pdp_info->qos_enc_len, pdp_info->qos_enc);
}
/* Update length field */
*len_field = msgb_length(msg) - old_len;
}
static void encode_auth_info(struct msgb *msg, enum osmo_gsup_iei iei,
const struct osmo_auth_vector *auth_vector)
{
uint8_t *len_field;
size_t old_len;
len_field = msgb_tlv_put(msg, iei, 0, NULL) - 1;
old_len = msgb_length(msg);
msgb_tlv_put(msg, OSMO_GSUP_RAND_IE,
sizeof(auth_vector->rand), auth_vector->rand);
msgb_tlv_put(msg, OSMO_GSUP_SRES_IE,
sizeof(auth_vector->sres), auth_vector->sres);
msgb_tlv_put(msg, OSMO_GSUP_KC_IE,
sizeof(auth_vector->kc), auth_vector->kc);
/* Update length field */
*len_field = msgb_length(msg) - old_len;
}
/*! Encode a GSUP message
* \param[out] msg message buffer to which encoded message is written
* \param[in] gsup_msg \ref osmo_gsup_message data to be encoded
*/
void osmo_gsup_encode(struct msgb *msg, const struct osmo_gsup_message *gsup_msg)
{
uint8_t u8;
int idx;
uint8_t bcd_buf[GSM48_MI_SIZE] = {0};
size_t bcd_len;
/* generic part */
OSMO_ASSERT(gsup_msg->message_type);
msgb_v_put(msg, gsup_msg->message_type);
bcd_len = gsm48_encode_bcd_number(bcd_buf, sizeof(bcd_buf), 0,
gsup_msg->imsi);
OSMO_ASSERT(bcd_len > 1);
/* Note that gsm48_encode_bcd_number puts the length into the first
* octet. Since msgb_tlv_put will add this length byte, we'll have to
* skip it */
msgb_tlv_put(msg, OSMO_GSUP_IMSI_IE, bcd_len - 1, &bcd_buf[1]);
/* specific parts */
if (gsup_msg->msisdn_enc)
msgb_tlv_put(msg, OSMO_GSUP_MSISDN_IE,
gsup_msg->msisdn_enc_len, gsup_msg->msisdn_enc);
if (gsup_msg->hlr_enc)
msgb_tlv_put(msg, OSMO_GSUP_HLR_NUMBER_IE,
gsup_msg->hlr_enc_len, gsup_msg->hlr_enc);
if ((u8 = gsup_msg->cause))
msgb_tlv_put(msg, OSMO_GSUP_CAUSE_IE, sizeof(u8), &u8);
if ((u8 = gsup_msg->cancel_type)) {
u8 -= 1;
msgb_tlv_put(msg, OSMO_GSUP_CANCEL_TYPE_IE, sizeof(u8), &u8);
}
if (gsup_msg->pdp_info_compl)
msgb_tlv_put(msg, OSMO_GSUP_PDP_INFO_COMPL_IE, 0, &u8);
if (gsup_msg->freeze_ptmsi)
msgb_tlv_put(msg, OSMO_GSUP_FREEZE_PTMSI_IE, 0, &u8);
for (idx = 0; idx < gsup_msg->num_pdp_infos; idx++) {
const struct osmo_gsup_pdp_info *pdp_info;
pdp_info = &gsup_msg->pdp_infos[idx];
if (pdp_info->context_id == 0)
continue;
if (pdp_info->have_info) {
encode_pdp_info(msg, OSMO_GSUP_PDP_INFO_IE, pdp_info);
} else {
u8 = pdp_info->context_id;
msgb_tlv_put(msg, OSMO_GSUP_PDP_CONTEXT_ID_IE,
sizeof(u8), &u8);
}
}
for (idx = 0; idx < gsup_msg->num_auth_vectors; idx++) {
const struct osmo_auth_vector *auth_vector;
auth_vector = &gsup_msg->auth_vectors[idx];
encode_auth_info(msg, OSMO_GSUP_AUTH_TUPLE_IE, auth_vector);
}
if (gsup_msg->auts)
msgb_tlv_put(msg, OSMO_GSUP_AUTS_IE, 16, gsup_msg->auts);
}

View File

@ -309,5 +309,8 @@ osmo_apn_qualify_from_imsi;
osmo_apn_to_str;
osmo_apn_from_str;
osmo_gsup_encode;
osmo_gsup_decode;
local: *;
};

View File

@ -13,7 +13,7 @@ check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_test \
vty/vty_test comp128/comp128_test utils/utils_test \
smscb/gsm0341_test stats/stats_test \
bitvec/bitvec_test msgb/msgb_test bits/bitcomp_test \
sim/sim_test tlv/tlv_test
sim/sim_test tlv/tlv_test gsup/gsup_test
if ENABLE_MSGFILE
check_PROGRAMS += msgfile/msgfile_test
@ -112,6 +112,9 @@ sim_sim_test_LDADD = $(top_builddir)/src/sim/libosmosim.la $(top_builddir)/src/l
tlv_tlv_test_SOURCES = tlv/tlv_test.c
tlv_tlv_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(top_builddir)/src/libosmocore.la
gsup_gsup_test_SOURCES = gsup/gsup_test.c
gsup_gsup_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(top_builddir)/src/libosmocore.la
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
:;{ \
@ -146,7 +149,7 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \
vty/vty_test.ok comp128/comp128_test.ok \
utils/utils_test.ok stats/stats_test.ok \
bitvec/bitvec_test.ok msgb/msgb_test.ok bits/bitcomp_test.ok \
sim/sim_test.ok tlv/tlv_test.ok
sim/sim_test.ok tlv/tlv_test.ok gsup/gsup_test.ok
DISTCLEANFILES = atconfig

264
tests/gsup/gsup_test.c Normal file
View File

@ -0,0 +1,264 @@
#include <string.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/application.h>
#include <osmocom/gsm/gsup.h>
#define VERBOSE_FPRINTF(...)
/* Tests for osmo_gsup_messages.c */
#define TEST_IMSI_IE 0x01, 0x08, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0xf5
#define TEST_IMSI_STR "123456789012345"
static void test_gsup_messages_dec_enc(void)
{
int test_idx;
int rc;
uint8_t buf[1024];
static const uint8_t send_auth_info_req[] = {
0x08,
TEST_IMSI_IE
};
static const uint8_t send_auth_info_err[] = {
0x09,
TEST_IMSI_IE,
0x02, 0x01, 0x07 /* GPRS no allowed */
};
static const uint8_t send_auth_info_res[] = {
0x0a,
TEST_IMSI_IE,
0x03, 0x22, /* Auth tuple */
0x20, 0x10,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
0x21, 0x04,
0x21, 0x22, 0x23, 0x24,
0x22, 0x08,
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
0x03, 0x22, /* Auth tuple */
0x20, 0x10,
0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90,
0x21, 0x04,
0xa1, 0xa2, 0xa3, 0xa4,
0x22, 0x08,
0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8,
};
static const uint8_t update_location_req[] = {
0x04,
TEST_IMSI_IE,
};
static const uint8_t update_location_err[] = {
0x05,
TEST_IMSI_IE,
0x02, 0x01, 0x07 /* GPRS no allowed */
};
static const uint8_t update_location_res[] = {
0x06,
TEST_IMSI_IE,
0x08, 0x07, /* MSISDN of the subscriber */
0x91, 0x94, 0x61, 0x46, 0x32, 0x24, 0x43,
0x09, 0x07, /* HLR-Number of the subscriber */
0x91, 0x83, 0x52, 0x38, 0x48, 0x83, 0x93,
0x04, 0x00, /* PDP info complete */
0x05, 0x15,
0x10, 0x01, 0x01,
0x11, 0x02, 0xf1, 0x21, /* IPv4 */
0x12, 0x09, 0x04, 't', 'e', 's', 't', 0x03, 'a', 'p', 'n',
0x13, 0x01, 0x02,
0x05, 0x11,
0x10, 0x01, 0x02,
0x11, 0x02, 0xf1, 0x21, /* IPv4 */
0x12, 0x08, 0x03, 'f', 'o', 'o', 0x03, 'a', 'p', 'n',
};
static const uint8_t location_cancellation_req[] = {
0x1c,
TEST_IMSI_IE,
0x06, 0x01, 0x00,
};
static const uint8_t location_cancellation_err[] = {
0x1d,
TEST_IMSI_IE,
0x02, 0x01, 0x03 /* Illegal MS */
};
static const uint8_t location_cancellation_res[] = {
0x1e,
TEST_IMSI_IE,
};
static const uint8_t purge_ms_req[] = {
0x0c,
TEST_IMSI_IE,
};
static const uint8_t purge_ms_err[] = {
0x0d,
TEST_IMSI_IE,
0x02, 0x01, 0x03, /* Illegal MS */
};
static const uint8_t purge_ms_res[] = {
0x0e,
TEST_IMSI_IE,
0x07, 0x00,
};
static const struct test {
char *name;
const uint8_t *data;
size_t data_len;
} test_messages[] = {
{"Send Authentication Info Request",
send_auth_info_req, sizeof(send_auth_info_req)},
{"Send Authentication Info Error",
send_auth_info_err, sizeof(send_auth_info_err)},
{"Send Authentication Info Result",
send_auth_info_res, sizeof(send_auth_info_res)},
{"Update Location Request",
update_location_req, sizeof(update_location_req)},
{"Update Location Error",
update_location_err, sizeof(update_location_err)},
{"Update Location Result",
update_location_res, sizeof(update_location_res)},
{"Location Cancellation Request",
location_cancellation_req, sizeof(location_cancellation_req)},
{"Location Cancellation Error",
location_cancellation_err, sizeof(location_cancellation_err)},
{"Location Cancellation Result",
location_cancellation_res, sizeof(location_cancellation_res)},
{"Purge MS Request",
purge_ms_req, sizeof(purge_ms_req)},
{"Purge MS Error",
purge_ms_err, sizeof(purge_ms_err)},
{"Purge MS Result",
purge_ms_res, sizeof(purge_ms_res)},
};
printf("Test GSUP message decoding/encoding\n");
for (test_idx = 0; test_idx < ARRAY_SIZE(test_messages); test_idx++) {
const struct test *t = &test_messages[test_idx];
struct osmo_gsup_message gm = {0};
struct msgb *msg = msgb_alloc(4096, "gsup_test");
printf(" Testing %s\n", t->name);
rc = osmo_gsup_decode(t->data, t->data_len, &gm);
OSMO_ASSERT(rc >= 0);
osmo_gsup_encode(msg, &gm);
fprintf(stderr, " generated message: %s\n", msgb_hexdump(msg));
fprintf(stderr, " original message: %s\n", osmo_hexdump(t->data, t->data_len));
fprintf(stderr, " IMSI: %s\n", gm.imsi);
OSMO_ASSERT(strcmp(gm.imsi, TEST_IMSI_STR) == 0);
OSMO_ASSERT(msgb_length(msg) == t->data_len);
OSMO_ASSERT(memcmp(msgb_data(msg), t->data, t->data_len) == 0);
msgb_free(msg);
}
/* simple truncation test */
for (test_idx = 0; test_idx < ARRAY_SIZE(test_messages); test_idx++) {
int j;
const struct test *t = &test_messages[test_idx];
int ie_end = t->data_len;
struct osmo_gsup_message gm = {0};
int counter = 0;
int parse_err = 0;
for (j = t->data_len - 1; j >= 0; --j) {
rc = osmo_gsup_decode(t->data, j, &gm);
counter += 1;
VERBOSE_FPRINTF(stderr,
" partial message decoding: "
"orig_len = %d, trunc = %d, rc = %d, ie_end = %d\n",
t->data_len, j, rc, ie_end);
if (rc >= 0) {
VERBOSE_FPRINTF(stderr,
" remaing partial message: %s\n",
osmo_hexdump(t->data + j, ie_end - j));
OSMO_ASSERT(j <= ie_end - 2);
OSMO_ASSERT(t->data[j+0] <= OSMO_GSUP_KC_IE);
OSMO_ASSERT(t->data[j+1] <= ie_end - j - 2);
ie_end = j;
} else {
parse_err += 1;
}
}
fprintf(stderr,
" message %d: tested %d truncations, %d parse failures\n",
test_idx, counter, parse_err);
}
/* message modification test (relies on ASAN or valgrind being used) */
for (test_idx = 0; test_idx < ARRAY_SIZE(test_messages); test_idx++) {
int j;
const struct test *t = &test_messages[test_idx];
struct osmo_gsup_message gm = {0};
uint8_t val;
int counter = 0;
int parse_err = 0;
OSMO_ASSERT(sizeof(buf) >= t->data_len);
for (j = t->data_len - 1; j >= 0; --j) {
memcpy(buf, t->data, t->data_len);
val = 0;
do {
VERBOSE_FPRINTF(stderr,
"t = %d, len = %d, val = %d\n",
test_idx, j, val);
buf[j] = val;
rc = osmo_gsup_decode(buf, t->data_len, &gm);
counter += 1;
if (rc < 0)
parse_err += 1;
val += 1;
} while (val != (uint8_t)256);
}
fprintf(stderr,
" message %d: tested %d modifications, %d parse failures\n",
test_idx, counter, parse_err);
}
}
const struct log_info_cat default_categories[] = {
[DLGSUP] = {
.name = "DLGSUP",
.description = "Generic Subscriber Update Protocol",
.enabled = 0, .loglevel = LOGL_DEBUG,
},
};
static struct log_info info = {
.cat = default_categories,
.num_cat = ARRAY_SIZE(default_categories),
};
int main(int argc, char **argv)
{
osmo_init_logging(&info);
test_gsup_messages_dec_enc();
printf("Done.\n");
return EXIT_SUCCESS;
}

14
tests/gsup/gsup_test.ok Normal file
View File

@ -0,0 +1,14 @@
Test GSUP message decoding/encoding
Testing Send Authentication Info Request
Testing Send Authentication Info Error
Testing Send Authentication Info Result
Testing Update Location Request
Testing Update Location Error
Testing Update Location Result
Testing Location Cancellation Request
Testing Location Cancellation Error
Testing Location Cancellation Result
Testing Purge MS Request
Testing Purge MS Error
Testing Purge MS Result
Done.

View File

@ -190,3 +190,9 @@ AT_KEYWORDS([tlv])
cat $abs_srcdir/tlv/tlv_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/tlv/tlv_test], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([gsup])
AT_KEYWORDS([gsup])
cat $abs_srcdir/gsup/gsup_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/gsup/gsup_test], [0], [expout], [ignore])
AT_CLEANUP