aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJacob Erlbeck <jerlbeck@sysmocom.de>2014-12-12 15:01:37 +0100
committerHolger Hans Peter Freyther <holger@moiji-mobile.com>2015-01-10 21:26:18 +0100
commita6ddc2d99f8463edb0b5eacf080db0b5ca571efc (patch)
tree0de9ea5d89c2e0a36cb4acbecdca73f24649159c
parentf3a271fa73ea58a628a51aa310e1da20a5526f31 (diff)
gprs: Add subscriber functions to create/handle GSUP messages
This patch extends gprs_subscr_query_auth_info and gprs_subscr_location_update to create GSUP messages with the help of a static gprs_subscr_tx_gsup_message function. A corresponding gprs_subscr_rx_gsup_message is added which takes a messages, gets the subscr, and updates it accordingly. Sponsored-by: On-Waves ehf [hfreyther: Added a msgb_free gprs_subscr_tx_gsup_message]
-rw-r--r--openbsc/include/openbsc/gprs_sgsn.h1
-rw-r--r--openbsc/src/gprs/gprs_subscriber.c211
-rw-r--r--openbsc/tests/sgsn/Makefile.am2
-rw-r--r--openbsc/tests/sgsn/sgsn_test.c165
-rw-r--r--openbsc/tests/sgsn/sgsn_test.ok1
5 files changed, 374 insertions, 6 deletions
diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h
index 61b84d873..8019ad1ca 100644
--- a/openbsc/include/openbsc/gprs_sgsn.h
+++ b/openbsc/include/openbsc/gprs_sgsn.h
@@ -315,6 +315,7 @@ struct gsm_subscriber *gprs_subscr_get_by_imsi(const char *imsi);
void gprs_subscr_put_and_cancel(struct gsm_subscriber *subscr);
void gprs_subscr_update(struct gsm_subscriber *subscr);
void gprs_subscr_update_auth_info(struct gsm_subscriber *subscr);
+int gprs_subscr_rx_gsup_message(struct msgb *msg);
/* Called on subscriber data updates */
void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx,
diff --git a/openbsc/src/gprs/gprs_subscriber.c b/openbsc/src/gprs/gprs_subscriber.c
index 1f210298f..461b3a573 100644
--- a/openbsc/src/gprs/gprs_subscriber.c
+++ b/openbsc/src/gprs/gprs_subscriber.c
@@ -24,6 +24,7 @@
#include <openbsc/sgsn.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/gprs_gmm.h>
+#include <openbsc/gprs_gsup_messages.h>
#include <openbsc/debug.h>
@@ -92,22 +93,220 @@ void gprs_subscr_put_and_cancel(struct gsm_subscriber *subscr)
gprs_subscr_delete(subscr);
}
-int gprs_subscr_query_auth_info(struct gsm_subscriber *subscr)
+static int gprs_subscr_tx_gsup_message(struct gsm_subscriber *subscr,
+ struct gprs_gsup_message *gsup_msg)
{
- /* TODO: Implement remote query to HLR, ... */
+ struct msgb *msg = msgb_alloc(4096, __func__);
+
+ strncpy(gsup_msg->imsi, subscr->imsi, sizeof(gsup_msg->imsi) - 1);
+
+ gprs_gsup_encode(msg, gsup_msg);
LOGMMCTXP(LOGL_INFO, subscr->sgsn_data->mm,
- "subscriber auth info is not available (remote query NYI)\n");
+ "Sending GSUP NYI, would send: %s\n", msgb_hexdump(msg));
+ msgb_free(msg);
+
return -ENOTSUP;
}
+static int gprs_subscr_handle_gsup_auth_res(struct gsm_subscriber *subscr,
+ struct gprs_gsup_message *gsup_msg)
+{
+ unsigned idx;
+ struct sgsn_subscriber_data *sdata = subscr->sgsn_data;
+
+ LOGP(DGPRS, LOGL_INFO,
+ "Got SendAuthenticationInfoResult, num_auth_tuples = %d\n",
+ gsup_msg->num_auth_tuples);
+
+ if (gsup_msg->num_auth_tuples > 0) {
+ memset(sdata->auth_triplets, 0, sizeof(sdata->auth_triplets));
+
+ for (idx = 0; idx < ARRAY_SIZE(sdata->auth_triplets); idx++)
+ sdata->auth_triplets[idx].key_seq = GSM_KEY_SEQ_INVAL;
+ }
+
+ for (idx = 0; idx < gsup_msg->num_auth_tuples; idx++) {
+ size_t key_seq = gsup_msg->auth_tuples[idx].key_seq;
+ LOGP(DGPRS, LOGL_DEBUG, "Adding auth tuple, cksn = %d\n", key_seq);
+ if (key_seq >= ARRAY_SIZE(sdata->auth_triplets)) {
+ LOGP(DGPRS, LOGL_NOTICE,
+ "Skipping auth triplet with invalid cksn %d\n",
+ key_seq);
+ continue;
+ }
+ sdata->auth_triplets[key_seq] = gsup_msg->auth_tuples[idx];
+ }
+
+ sdata->auth_triplets_updated = 1;
+
+ gprs_subscr_update_auth_info(subscr);
+
+ return 0;
+}
+
+static int gprs_subscr_handle_gsup_upd_loc_res(struct gsm_subscriber *subscr,
+ struct gprs_gsup_message *gsup_msg)
+{
+ unsigned idx;
+
+ if (gsup_msg->pdp_info_compl) {
+ LOGP(DGPRS, LOGL_INFO, "Would clear existing PDP info\n");
+
+ /* TODO: clear existing PDP info entries */
+ }
+
+ for (idx = 0; idx < gsup_msg->num_pdp_infos; idx++) {
+ struct gprs_gsup_pdp_info *pdp_info = &gsup_msg->pdp_infos[idx];
+ size_t ctx_id = pdp_info->context_id;
+
+ LOGP(DGPRS, LOGL_INFO,
+ "Would set PDP info, context id = %d, APN = %s\n",
+ ctx_id, osmo_hexdump(pdp_info->apn_enc, pdp_info->apn_enc_len));
+
+ /* TODO: set PDP info [ctx_id] */
+ }
+
+ subscr->authorized = 1;
+
+ gprs_subscr_update(subscr);
+ return 0;
+}
+
+static int gprs_subscr_handle_gsup_auth_err(struct gsm_subscriber *subscr,
+ struct gprs_gsup_message *gsup_msg)
+{
+ unsigned idx;
+ struct sgsn_subscriber_data *sdata = subscr->sgsn_data;
+
+ LOGP(DGPRS, LOGL_INFO,
+ "Send authentication info has failed for IMSI %s with cause %d\n",
+ gsup_msg->imsi, gsup_msg->cause);
+
+ /* Clear auth tuples */
+ memset(sdata->auth_triplets, 0, sizeof(sdata->auth_triplets));
+ for (idx = 0; idx < ARRAY_SIZE(sdata->auth_triplets); idx++)
+ sdata->auth_triplets[idx].key_seq = GSM_KEY_SEQ_INVAL;
+
+ subscr->authorized = 0;
+
+ gprs_subscr_update_auth_info(subscr);
+ return 0;
+}
+
+static int gprs_subscr_handle_gsup_upd_loc_err(struct gsm_subscriber *subscr,
+ struct gprs_gsup_message *gsup_msg)
+{
+ LOGP(DGPRS, LOGL_INFO,
+ "Update location has failed for IMSI %s with cause %d\n",
+ gsup_msg->imsi, gsup_msg->cause);
+
+ subscr->authorized = 0;
+
+ gprs_subscr_update(subscr);
+ return 0;
+}
+
+int gprs_subscr_rx_gsup_message(struct msgb *msg)
+{
+ uint8_t *data = msgb_l2(msg);
+ size_t data_len = msgb_l2len(msg);
+ int rc = 0;
+
+ struct gprs_gsup_message gsup_msg = {0};
+ struct gsm_subscriber *subscr;
+
+ rc = gprs_gsup_decode(data, data_len, &gsup_msg);
+ if (rc < 0) {
+ LOGP(DGPRS, LOGL_ERROR,
+ "decoding GSUP message fails with error code %d\n", -rc);
+ return rc;
+ }
+
+ if (!gsup_msg.imsi[0])
+ return -GMM_CAUSE_INV_MAND_INFO;
+
+ if (gsup_msg.message_type == GPRS_GSUP_MSGT_INSERT_DATA_REQUEST)
+ subscr = gprs_subscr_get_or_create(gsup_msg.imsi);
+ else
+ subscr = gprs_subscr_get_by_imsi(gsup_msg.imsi);
+
+ if (!subscr) {
+ LOGP(DGPRS, LOGL_NOTICE,
+ "Unknown IMSI %s, discarding GSUP message\n", gsup_msg.imsi);
+ return -GMM_CAUSE_IMSI_UNKNOWN;
+ }
+
+ LOGP(DGPRS, LOGL_INFO,
+ "Received GSUP message of type 0x%02x for IMSI %s\n",
+ gsup_msg.message_type, gsup_msg.imsi);
+
+ switch (gsup_msg.message_type) {
+ case GPRS_GSUP_MSGT_LOCATION_CANCEL_REQUEST:
+ gprs_subscr_put_and_cancel(subscr);
+ subscr = NULL;
+ break;
+
+ case GPRS_GSUP_MSGT_SEND_AUTH_INFO_RESULT:
+ gprs_subscr_handle_gsup_auth_res(subscr, &gsup_msg);
+ break;
+
+ case GPRS_GSUP_MSGT_SEND_AUTH_INFO_ERROR:
+ gprs_subscr_handle_gsup_auth_err(subscr, &gsup_msg);
+ break;
+
+ case GPRS_GSUP_MSGT_UPDATE_LOCATION_RESULT:
+ gprs_subscr_handle_gsup_upd_loc_res(subscr, &gsup_msg);
+ break;
+
+ case GPRS_GSUP_MSGT_UPDATE_LOCATION_ERROR:
+ gprs_subscr_handle_gsup_upd_loc_err(subscr, &gsup_msg);
+ break;
+
+ case GPRS_GSUP_MSGT_PURGE_MS_ERROR:
+ case GPRS_GSUP_MSGT_PURGE_MS_RESULT:
+ case GPRS_GSUP_MSGT_INSERT_DATA_REQUEST:
+ case GPRS_GSUP_MSGT_DELETE_DATA_REQUEST:
+ LOGP(DGPRS, LOGL_ERROR,
+ "Rx GSUP message type %d not yet implemented\n",
+ gsup_msg.message_type);
+ rc = -GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL;
+ break;
+
+ default:
+ LOGP(DGPRS, LOGL_ERROR,
+ "Rx GSUP message type %d not valid at SGSN\n",
+ gsup_msg.message_type);
+ rc = -GMM_CAUSE_MSGT_INCOMP_P_STATE;
+ break;
+ };
+
+ if (subscr)
+ subscr_put(subscr);
+
+ return rc;
+}
+
+int gprs_subscr_query_auth_info(struct gsm_subscriber *subscr)
+{
+ struct gprs_gsup_message gsup_msg = {0};
+
+ LOGMMCTXP(LOGL_INFO, subscr->sgsn_data->mm,
+ "subscriber auth info is not available\n");
+
+ gsup_msg.message_type = GPRS_GSUP_MSGT_SEND_AUTH_INFO_REQUEST;
+ return gprs_subscr_tx_gsup_message(subscr, &gsup_msg);
+}
+
int gprs_subscr_location_update(struct gsm_subscriber *subscr)
{
- /* TODO: Implement remote query to HLR, ... */
+ struct gprs_gsup_message gsup_msg = {0};
LOGMMCTXP(LOGL_INFO, subscr->sgsn_data->mm,
- "subscriber data is not available (remote query NYI)\n");
- return -ENOTSUP;
+ "subscriber data is not available\n");
+
+ gsup_msg.message_type = GPRS_GSUP_MSGT_UPDATE_LOCATION_REQUEST;
+ return gprs_subscr_tx_gsup_message(subscr, &gsup_msg);
}
void gprs_subscr_update(struct gsm_subscriber *subscr)
diff --git a/openbsc/tests/sgsn/Makefile.am b/openbsc/tests/sgsn/Makefile.am
index 50c37d627..c8dccfe07 100644
--- a/openbsc/tests/sgsn/Makefile.am
+++ b/openbsc/tests/sgsn/Makefile.am
@@ -21,6 +21,8 @@ sgsn_test_LDADD = \
$(top_builddir)/src/gprs/sgsn_vty.o \
$(top_builddir)/src/gprs/sgsn_libgtp.o \
$(top_builddir)/src/gprs/sgsn_auth.o \
+ $(top_builddir)/src/gprs/gprs_gsup_messages.o \
+ $(top_builddir)/src/gprs/gprs_utils.o \
$(top_builddir)/src/gprs/gprs_subscriber.o \
$(top_builddir)/src/libcommon/libcommon.a \
$(LIBOSMOCORE_LIBS) \
diff --git a/openbsc/tests/sgsn/sgsn_test.c b/openbsc/tests/sgsn/sgsn_test.c
index c7eec7bde..391cb6326 100644
--- a/openbsc/tests/sgsn/sgsn_test.c
+++ b/openbsc/tests/sgsn/sgsn_test.c
@@ -322,6 +322,170 @@ static void test_auth_triplets(void)
gprs_llgmm_assign(llme, local_tlli, 0xffffffff, GPRS_ALGO_GEA0, NULL);
}
+#define TEST_GSUP_IMSI1_IE 0x01, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09
+
+static void test_subscriber_gsup(void)
+{
+ struct gsm_subscriber *s1, *s1found;
+ const char *imsi1 = "1234567890";
+ struct sgsn_mm_ctx *ctx;
+ struct gprs_ra_id raid = { 0, };
+ uint32_t local_tlli = 0xffeeddcc;
+ struct gprs_llc_llme *llme;
+ struct msgb *msg;
+ int rc;
+
+ static const uint8_t send_auth_info_res[] = {
+ 0x0a,
+ TEST_GSUP_IMSI1_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 send_auth_info_err[] = {
+ 0x09,
+ TEST_GSUP_IMSI1_IE,
+ 0x02, 0x01, 0x07 /* GPRS not allowed */
+ };
+
+ static const uint8_t update_location_res[] = {
+ 0x06,
+ TEST_GSUP_IMSI1_IE,
+ 0x04, 0x00, /* PDP info complete */
+ 0x05, 0x12,
+ 0x10, 0x01, 0x01,
+ 0x11, 0x02, 0xf1, 0x21, /* IPv4 */
+ 0x12, 0x09, 0x04, 't', 'e', 's', 't', 0x03, 'a', 'p', 'n',
+ 0x05, 0x11,
+ 0x10, 0x01, 0x02,
+ 0x11, 0x02, 0xf1, 0x21, /* IPv4 */
+ 0x12, 0x08, 0x03, 'f', 'o', 'o', 0x03, 'a', 'p', 'n',
+ };
+
+ static const uint8_t update_location_err[] = {
+ 0x05,
+ TEST_GSUP_IMSI1_IE,
+ 0x02, 0x01, 0x07 /* GPRS not allowed */
+ };
+
+ static const uint8_t location_cancellation_req[] = {
+ 0x1c,
+ TEST_GSUP_IMSI1_IE,
+ 0x06, 0x01, 0x00,
+ };
+
+ printf("Testing subcriber GSUP handling\n");
+
+ update_subscriber_data_cb = my_dummy_sgsn_update_subscriber_data;
+
+ /* Check for emptiness */
+ OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL);
+
+ /* Allocate entry 1 */
+ s1 = gprs_subscr_get_or_create(imsi1);
+ s1->flags |= GSM_SUBSCRIBER_FIRST_CONTACT;
+ s1found = gprs_subscr_get_by_imsi(imsi1);
+ OSMO_ASSERT(s1found == s1);
+ subscr_put(s1found);
+
+ /* Create a context */
+ OSMO_ASSERT(count(gprs_llme_list()) == 0);
+ ctx = alloc_mm_ctx(local_tlli, &raid);
+ llme = ctx->llme;
+
+ /* Attach s1 to ctx */
+ ctx->subscr = subscr_get(s1);
+ ctx->subscr->sgsn_data->mm = ctx;
+
+ /* Inject SendAuthInfoReq GSUP message */
+ msg = msgb_alloc(1024, __func__);
+ msg->l2h = msgb_put(msg, sizeof(send_auth_info_res));
+ memcpy(msg->l2h, send_auth_info_res, sizeof(send_auth_info_res));
+ rc = gprs_subscr_rx_gsup_message(msg);
+ msgb_free(msg);
+ OSMO_ASSERT(rc >= 0);
+ OSMO_ASSERT(last_updated_subscr == s1);
+
+ /* Check triplets */
+ OSMO_ASSERT(s1->sgsn_data->auth_triplets[0].key_seq == 0);
+ OSMO_ASSERT(s1->sgsn_data->auth_triplets[1].key_seq == 1);
+ OSMO_ASSERT(s1->sgsn_data->auth_triplets[2].key_seq == GSM_KEY_SEQ_INVAL);
+
+ /* Inject SendAuthInfoErr GSUP message */
+ msg = msgb_alloc(1024, __func__);
+ msg->l2h = msgb_put(msg, sizeof(send_auth_info_err));
+ memcpy(msg->l2h, send_auth_info_err, sizeof(send_auth_info_err));
+ rc = gprs_subscr_rx_gsup_message(msg);
+ msgb_free(msg);
+ OSMO_ASSERT(rc >= 0);
+ OSMO_ASSERT(last_updated_subscr == s1);
+
+ /* Check triplets */
+ OSMO_ASSERT(s1->sgsn_data->auth_triplets[0].key_seq == GSM_KEY_SEQ_INVAL);
+ OSMO_ASSERT(s1->sgsn_data->auth_triplets[1].key_seq == GSM_KEY_SEQ_INVAL);
+ OSMO_ASSERT(s1->sgsn_data->auth_triplets[2].key_seq == GSM_KEY_SEQ_INVAL);
+
+ /* Inject UpdateLocReq GSUP message */
+ msg = msgb_alloc(1024, __func__);
+ msg->l2h = msgb_put(msg, sizeof(update_location_res));
+ memcpy(msg->l2h, update_location_res, sizeof(update_location_res));
+ rc = gprs_subscr_rx_gsup_message(msg);
+ msgb_free(msg);
+ OSMO_ASSERT(rc >= 0);
+ OSMO_ASSERT(last_updated_subscr == s1);
+
+ /* Check authorization */
+ OSMO_ASSERT(s1->authorized == 1);
+
+ /* Inject UpdateLocErr GSUP message */
+ msg = msgb_alloc(1024, __func__);
+ msg->l2h = msgb_put(msg, sizeof(update_location_err));
+ memcpy(msg->l2h, update_location_err, sizeof(update_location_err));
+ rc = gprs_subscr_rx_gsup_message(msg);
+ msgb_free(msg);
+ OSMO_ASSERT(rc >= 0);
+ OSMO_ASSERT(last_updated_subscr == s1);
+
+ /* Check authorization */
+ OSMO_ASSERT(s1->authorized == 0);
+
+ /* Inject UpdateLocReq GSUP message */
+ msg = msgb_alloc(1024, __func__);
+ msg->l2h = msgb_put(msg, sizeof(location_cancellation_req));
+ memcpy(msg->l2h,
+ location_cancellation_req, sizeof(location_cancellation_req));
+ rc = gprs_subscr_rx_gsup_message(msg);
+ msgb_free(msg);
+ OSMO_ASSERT(rc >= 0);
+ OSMO_ASSERT(last_updated_subscr == s1);
+
+ /* Check cancellation result */
+ OSMO_ASSERT(s1->flags & GPRS_SUBSCRIBER_CANCELLED);
+ OSMO_ASSERT(s1->sgsn_data->mm == NULL);
+
+ /* Free MM context and subscriber */
+ subscr_put(s1);
+ s1found = gprs_subscr_get_by_imsi(imsi1);
+ OSMO_ASSERT(s1found == NULL);
+ gprs_llgmm_assign(llme, local_tlli, 0xffffffff, GPRS_ALGO_GEA0, NULL);
+
+ update_subscriber_data_cb = __real_sgsn_update_subscriber_data;
+}
+
/*
* Test that a GMM Detach will remove the MMCTX and the
* associated LLME.
@@ -1283,6 +1447,7 @@ int main(int argc, char **argv)
test_llme();
test_subscriber();
test_auth_triplets();
+ test_subscriber_gsup();
test_gmm_detach();
test_gmm_detach_power_off();
test_gmm_detach_no_mmctx();
diff --git a/openbsc/tests/sgsn/sgsn_test.ok b/openbsc/tests/sgsn/sgsn_test.ok
index e54991cb5..b118328df 100644
--- a/openbsc/tests/sgsn/sgsn_test.ok
+++ b/openbsc/tests/sgsn/sgsn_test.ok
@@ -1,6 +1,7 @@
Testing LLME allocations
Testing core subscriber data API
Testing authentication triplet handling
+Testing subcriber GSUP handling
Testing GMM detach
Testing GMM detach (power off)
Testing GMM detach (no MMCTX)