diff options
author | Jacob Erlbeck <jerlbeck@sysmocom.de> | 2014-12-12 15:01:37 +0100 |
---|---|---|
committer | Holger Hans Peter Freyther <holger@moiji-mobile.com> | 2015-01-10 21:26:18 +0100 |
commit | a6ddc2d99f8463edb0b5eacf080db0b5ca571efc (patch) | |
tree | 0de9ea5d89c2e0a36cb4acbecdca73f24649159c /openbsc | |
parent | f3a271fa73ea58a628a51aa310e1da20a5526f31 (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]
Diffstat (limited to 'openbsc')
-rw-r--r-- | openbsc/include/openbsc/gprs_sgsn.h | 1 | ||||
-rw-r--r-- | openbsc/src/gprs/gprs_subscriber.c | 211 | ||||
-rw-r--r-- | openbsc/tests/sgsn/Makefile.am | 2 | ||||
-rw-r--r-- | openbsc/tests/sgsn/sgsn_test.c | 165 | ||||
-rw-r--r-- | openbsc/tests/sgsn/sgsn_test.ok | 1 |
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) |