diff options
Diffstat (limited to 'tests/sgsn/sgsn_test.c')
-rw-r--r-- | tests/sgsn/sgsn_test.c | 2487 |
1 files changed, 2487 insertions, 0 deletions
diff --git a/tests/sgsn/sgsn_test.c b/tests/sgsn/sgsn_test.c new file mode 100644 index 000000000..2f1513a29 --- /dev/null +++ b/tests/sgsn/sgsn_test.c @@ -0,0 +1,2487 @@ +/* Test the SGSN */ +/* + * (C) 2014 by Holger Hans Peter Freyther + * (C) 2014 by sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * 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 <openbsc/gprs_llc.h> +#include <openbsc/sgsn.h> +#include <openbsc/gprs_gmm.h> +#include <openbsc/debug.h> +#include <openbsc/gprs_subscriber.h> +#include <osmocom/gsm/gsup.h> +#include <openbsc/gsup_client.h> +#include <openbsc/gprs_utils.h> +#include <openbsc/gprs_gb_parse.h> + +#include <osmocom/gprs/gprs_bssgp.h> + +#include <osmocom/gsm/gsm_utils.h> + +#include <osmocom/core/application.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/core/utils.h> + +#include <stdio.h> + +void *tall_bsc_ctx; +static struct sgsn_instance sgsn_inst = { + .config_file = "osmo_sgsn.cfg", + .cfg = { + .gtp_statedir = "./", + .auth_policy = SGSN_AUTH_POLICY_CLOSED, + }, +}; +struct sgsn_instance *sgsn = &sgsn_inst; +unsigned sgsn_tx_counter = 0; +struct msgb *last_msg = NULL; +struct gprs_gb_parse_context last_dl_parse_ctx; + +static void reset_last_msg() +{ + if (last_msg) + msgb_free(last_msg); + + last_msg = NULL; + memset(&last_dl_parse_ctx, 0, sizeof(last_dl_parse_ctx)); +} + +static void cleanup_test() +{ + reset_last_msg(); +} + +static uint32_t get_new_ptmsi(const struct gprs_gb_parse_context *parse_ctx) +{ + uint32_t new_ptmsi = GSM_RESERVED_TMSI; + + if (parse_ctx->new_ptmsi_enc) + gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi); + + return new_ptmsi; +} + +/* override */ +int bssgp_tx_dl_ud(struct msgb *msg, uint16_t pdu_lifetime, + struct bssgp_dl_ud_par *dup) +{ + int rc; + + reset_last_msg(); + + last_msg = msg; + OSMO_ASSERT(msgb_data(last_msg) != NULL); + + rc = gprs_gb_parse_llc(msgb_data(last_msg), msgb_length(last_msg), + &last_dl_parse_ctx); + + fprintf(stderr, "Got DL LLC message: %s\n", + gprs_gb_message_name(&last_dl_parse_ctx, "UNKNOWN")); + + OSMO_ASSERT(rc > 0); + + sgsn_tx_counter += 1; + return 0; +} + +/* override, requires '-Wl,--wrap=RAND_bytes' */ +int __real_RAND_bytes(unsigned char *buf, int num); +int mock_RAND_bytes(unsigned char *buf, int num); +int (*RAND_bytes_cb)(unsigned char *, int) = + &mock_RAND_bytes; + +int __wrap_RAND_bytes(unsigned char *buf, int num) +{ + return (*RAND_bytes_cb)(buf, num); +} +/* make results of A&C ref predictable */ +int mock_RAND_bytes(unsigned char *buf, int num) +{ + if (num > 1) + return __real_RAND_bytes(buf, num); + buf[0] = 0; + return 1; +} + +/* override, requires '-Wl,--wrap=sgsn_update_subscriber_data' */ +void __real_sgsn_update_subscriber_data(struct sgsn_mm_ctx *); +void (*update_subscriber_data_cb)(struct sgsn_mm_ctx *) = + &__real_sgsn_update_subscriber_data; + +void __wrap_sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx) +{ + (*update_subscriber_data_cb)(mmctx); +} + +/* override, requires '-Wl,--wrap=gprs_subscr_request_update_location' */ +int __real_gprs_subscr_request_update_location(struct sgsn_mm_ctx *mmctx); +int (*subscr_request_update_location_cb)(struct sgsn_mm_ctx *mmctx) = + &__real_gprs_subscr_request_update_location; + +int __wrap_gprs_subscr_request_update_location(struct sgsn_mm_ctx *mmctx) { + return (*subscr_request_update_location_cb)(mmctx); +}; + +/* override, requires '-Wl,--wrap=gprs_subscr_request_auth_info' */ +int __real_gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx); +int (*subscr_request_auth_info_cb)(struct sgsn_mm_ctx *mmctx) = + &__real_gprs_subscr_request_auth_info; + +int __wrap_gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx) { + return (*subscr_request_auth_info_cb)(mmctx); +}; + +/* override, requires '-Wl,--wrap=gsup_client_send' */ +int __real_gsup_client_send(struct gsup_client *gsupc, struct msgb *msg); +int (*gsup_client_send_cb)(struct gsup_client *gsupc, struct msgb *msg) = + &__real_gsup_client_send; + +int __wrap_gsup_client_send(struct gsup_client *gsupc, struct msgb *msg) +{ + return (*gsup_client_send_cb)(gsupc, msg); +}; + +static int count(struct llist_head *head) +{ + struct llist_head *cur; + int count = 0; + + llist_for_each(cur, head) + count += 1; + + return count; +} + +static struct msgb *create_msg(const uint8_t *data, size_t len) +{ + struct msgb *msg = msgb_alloc(len + 8, "test message"); + msg->l1h = msgb_put(msg, 8); + msg->l2h = msgb_put(msg, len); + memcpy(msg->l2h, data, len); + + msgb_bcid(msg) = msg->l1h; + msgb_gmmh(msg) = msg->l2h; + return msg; +} + +/* + * Create a context and search for it + */ +static struct sgsn_mm_ctx *alloc_mm_ctx(uint32_t tlli, struct gprs_ra_id *raid) +{ + struct sgsn_mm_ctx *ctx, *ictx; + struct gprs_llc_lle *lle; + int old_count = count(gprs_llme_list()); + + lle = gprs_lle_get_or_create(tlli, 3); + ctx = sgsn_mm_ctx_alloc_gb(tlli, raid); + ctx->gmm_state = GMM_REGISTERED_NORMAL; + ctx->gb.llme = lle->llme; + + ictx = sgsn_mm_ctx_by_tlli(tlli, raid); + OSMO_ASSERT(ictx == ctx); + + OSMO_ASSERT(count(gprs_llme_list()) == old_count + 1); + + return ctx; +} + +static void send_0408_message(struct gprs_llc_llme *llme, uint32_t tlli, + const struct gprs_ra_id *bssgp_raid, + const uint8_t *data, size_t data_len) +{ + struct msgb *msg; + + reset_last_msg(); + sgsn_tx_counter = 0; + + msg = create_msg(data, data_len); + msgb_tlli(msg) = tlli; + bssgp_create_cell_id(msgb_bcid(msg), bssgp_raid, 0); + gsm0408_gprs_rcvmsg_gb(msg, llme, false); + msgb_free(msg); +} + +static void test_llme(void) +{ + struct gprs_llc_lle *lle, *lle_copy; + uint32_t local_tlli; + + printf("Testing LLME allocations\n"); + local_tlli = gprs_tmsi2tlli(0x234, TLLI_LOCAL); + + /* initial state */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + + /* Create a new entry */ + lle = gprs_lle_get_or_create(local_tlli, 3); + OSMO_ASSERT(lle); + OSMO_ASSERT(count(gprs_llme_list()) == 1); + + /* No new entry is created */ + lle_copy = gprs_lle_get_or_create(local_tlli, 3); + OSMO_ASSERT(lle == lle_copy); + OSMO_ASSERT(count(gprs_llme_list()) == 1); + + /* unassign which should delete it*/ + gprs_llgmm_unassign(lle->llme); + + /* Check that everything was cleaned up */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + + cleanup_test(); +} + +struct gprs_subscr *last_updated_subscr = NULL; +void my_dummy_sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx) +{ + OSMO_ASSERT(mmctx); + fprintf(stderr, "Called %s, mmctx = %p, subscr = %p\n", + __func__, mmctx, mmctx->subscr); + last_updated_subscr = mmctx->subscr; +} + +static void assert_subscr(const struct gprs_subscr *subscr, const char *imsi) +{ + struct gprs_subscr *sfound; + OSMO_ASSERT(subscr); + OSMO_ASSERT(strcmp(subscr->imsi, imsi) == 0); + + sfound = gprs_subscr_get_by_imsi(imsi); + OSMO_ASSERT(sfound == subscr); + + gprs_subscr_put(sfound); +} + +static void show_subscrs(FILE *out) +{ + struct gprs_subscr *subscr; + + llist_for_each_entry(subscr, gprs_subscribers, entry) { + fprintf(out, " Subscriber: %s, " + "use count: %d\n", + subscr->imsi, subscr->use_count); + } +} + +static void assert_no_subscrs() +{ + show_subscrs(stdout); + fflush(stdout); + OSMO_ASSERT(llist_empty(gprs_subscribers)); +} + +#define VERBOSE_ASSERT(val, expect_op, fmt) \ + do { \ + printf(#val " == " fmt "\n", (val)); \ + OSMO_ASSERT((val) expect_op); \ + } while (0); + +static void test_subscriber(void) +{ + struct gprs_subscr *s1, *s2, *s3; + const char *imsi1 = "1234567890"; + const char *imsi2 = "9876543210"; + const char *imsi3 = "5656565656"; + + update_subscriber_data_cb = my_dummy_sgsn_update_subscriber_data; + + printf("Testing core subscriber data API\n"); + + /* Check for emptiness */ + OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL); + OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi2) == NULL); + OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi3) == NULL); + VERBOSE_ASSERT(llist_count(gprs_subscribers), == 0, "%d"); + + /* Allocate entry 1 */ + s1 = gprs_subscr_get_or_create(imsi1); + VERBOSE_ASSERT(llist_count(gprs_subscribers), == 1, "%d"); + s1->flags |= GPRS_SUBSCRIBER_FIRST_CONTACT; + assert_subscr(s1, imsi1); + VERBOSE_ASSERT(llist_count(gprs_subscribers), == 1, "%d"); + OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi2) == NULL); + + /* Allocate entry 2 */ + s2 = gprs_subscr_get_or_create(imsi2); + VERBOSE_ASSERT(llist_count(gprs_subscribers), == 2, "%d"); + s2->flags |= GPRS_SUBSCRIBER_FIRST_CONTACT; + + /* Allocate entry 3 */ + s3 = gprs_subscr_get_or_create(imsi3); + VERBOSE_ASSERT(llist_count(gprs_subscribers), == 3, "%d"); + + /* Check entries */ + assert_subscr(s1, imsi1); + assert_subscr(s2, imsi2); + assert_subscr(s3, imsi3); + + /* Update entry 1 */ + last_updated_subscr = NULL; + gprs_subscr_update(s1); + OSMO_ASSERT(last_updated_subscr == NULL); + OSMO_ASSERT(s1->sgsn_data->mm == NULL); + OSMO_ASSERT((s1->flags & GPRS_SUBSCRIBER_FIRST_CONTACT) == 0); + + /* There is no subscriber cache. Verify it */ + gprs_subscr_cleanup(s1); + gprs_subscr_put(s1); + s1 = NULL; + VERBOSE_ASSERT(llist_count(gprs_subscribers), == 2, "%d"); + OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL); + + assert_subscr(s2, imsi2); + assert_subscr(s3, imsi3); + + /* Free entry 2 (GPRS_SUBSCRIBER_FIRST_CONTACT is set) */ + gprs_subscr_cleanup(s2); + gprs_subscr_put(s2); + s2 = NULL; + VERBOSE_ASSERT(llist_count(gprs_subscribers), == 1, "%d"); + OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL); + OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi2) == NULL); + assert_subscr(s3, imsi3); + + /* Try to delete entry 3 */ + gprs_subscr_cleanup(s3); + gprs_subscr_put(s3); + s3 = NULL; + VERBOSE_ASSERT(llist_count(gprs_subscribers), == 0, "%d"); + OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi3) == NULL); + + OSMO_ASSERT(llist_empty(gprs_subscribers)); + + update_subscriber_data_cb = __real_sgsn_update_subscriber_data; + + cleanup_test(); +} + +static void test_auth_triplets(void) +{ + struct gprs_subscr *s1, *s1found; + const char *imsi1 = "1234567890"; + struct gsm_auth_tuple *at; + struct sgsn_mm_ctx *ctx; + struct gprs_ra_id raid = { 0, }; + uint32_t local_tlli = 0xffeeddcc; + + printf("Testing authentication triplet handling\n"); + + /* Check for emptiness */ + OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL); + + /* Allocate entry 1 */ + s1 = gprs_subscr_get_or_create(imsi1); + s1->flags |= GPRS_SUBSCRIBER_FIRST_CONTACT; + s1found = gprs_subscr_get_by_imsi(imsi1); + OSMO_ASSERT(s1found == s1); + gprs_subscr_put(s1found); + + /* Create a context */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + ctx = alloc_mm_ctx(local_tlli, &raid); + + /* Attach s1 to ctx */ + ctx->subscr = gprs_subscr_get(s1); + ctx->subscr->sgsn_data->mm = ctx; + + /* Try to get auth tuple */ + at = sgsn_auth_get_tuple(ctx, GSM_KEY_SEQ_INVAL); + OSMO_ASSERT(at == NULL); + + /* Add triplets */ + s1->sgsn_data->auth_triplets[0].key_seq = 0; + s1->sgsn_data->auth_triplets[1].key_seq = 1; + s1->sgsn_data->auth_triplets[2].key_seq = 2; + + /* Try to get auth tuple */ + at = sgsn_auth_get_tuple(ctx, GSM_KEY_SEQ_INVAL); + OSMO_ASSERT(at != NULL); + OSMO_ASSERT(at->key_seq == 0); + OSMO_ASSERT(at->use_count == 1); + at = sgsn_auth_get_tuple(ctx, at->key_seq); + OSMO_ASSERT(at != NULL); + OSMO_ASSERT(at->key_seq == 1); + OSMO_ASSERT(at->use_count == 1); + at = sgsn_auth_get_tuple(ctx, at->key_seq); + OSMO_ASSERT(at != NULL); + OSMO_ASSERT(at->key_seq == 2); + OSMO_ASSERT(at->use_count == 1); + at = sgsn_auth_get_tuple(ctx, at->key_seq); + OSMO_ASSERT(at == NULL); + + /* Free MM context and subscriber */ + gprs_subscr_put(s1); + sgsn_mm_ctx_cleanup_free(ctx); + s1found = gprs_subscr_get_by_imsi(imsi1); + OSMO_ASSERT(s1found == NULL); + + cleanup_test(); +} + +#define TEST_GSUP_IMSI1_IE 0x01, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09 + +static int rx_gsup_message(const uint8_t *data, size_t data_len) +{ + struct msgb *msg; + int rc; + + msg = msgb_alloc(1024, __func__); + msg->l2h = msgb_put(msg, data_len); + OSMO_ASSERT(msg->l2h != NULL); + memcpy(msg->l2h, data, data_len); + rc = gprs_subscr_rx_gsup_message(msg); + msgb_free(msg); + + return rc; +} + +static void test_subscriber_gsup(void) +{ + struct gprs_subscr *s1, *s1found; + const char *imsi1 = "1234567890"; + struct sgsn_mm_ctx *ctx; + struct gprs_ra_id raid = { 0, }; + uint32_t local_tlli = 0xffeeddcc; + struct sgsn_subscriber_pdp_data *pdpd; + 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 */ + }; + +#define MSISDN 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 + + static const uint8_t s1_msisdn[] = { MSISDN }; + + static const uint8_t update_location_res[] = { + 0x06, + TEST_GSUP_IMSI1_IE, + 0x08, 0x09, MSISDN, + 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', + }; + +#undef MSISDN + + 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, + }; + + static const uint8_t location_cancellation_req_withdraw[] = { + 0x1c, + TEST_GSUP_IMSI1_IE, + 0x06, 0x01, 0x01, + }; + + static const uint8_t location_cancellation_req_other[] = { + 0x1c, + 0x01, 0x05, 0x11, 0x11, 0x11, 0x11, 0x01, + 0x06, 0x01, 0x00, + }; + + static const uint8_t purge_ms_err[] = { + 0x0d, + TEST_GSUP_IMSI1_IE, + 0x02, 0x01, 0x02, /* IMSI unknown in HLR */ + }; + + static const uint8_t purge_ms_err_no_cause[] = { + 0x0d, + TEST_GSUP_IMSI1_IE, + }; + + static const uint8_t purge_ms_res[] = { + 0x0e, + TEST_GSUP_IMSI1_IE, + 0x07, 0x00, + }; + + + static const uint8_t insert_data_req[] = { + 0x10, + TEST_GSUP_IMSI1_IE, + 0x05, 0x11, + 0x10, 0x01, 0x03, + 0x11, 0x02, 0xf1, 0x21, /* IPv4 */ + 0x12, 0x08, 0x03, 'b', 'a', 'r', 0x03, 'a', 'p', 'n', + }; + + static const uint8_t delete_data_req[] = { + 0x14, + TEST_GSUP_IMSI1_IE, + 0x10, 0x01, 0x03, + }; + + printf("Testing subscriber 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 |= GPRS_SUBSCRIBER_FIRST_CONTACT; + s1found = gprs_subscr_get_by_imsi(imsi1); + OSMO_ASSERT(s1found == s1); + gprs_subscr_put(s1found); + + /* Create a context */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + ctx = alloc_mm_ctx(local_tlli, &raid); + + /* Attach s1 to ctx */ + ctx->subscr = gprs_subscr_get(s1); + ctx->subscr->sgsn_data->mm = ctx; + + /* Inject SendAuthInfoReq GSUP message */ + rc = rx_gsup_message(send_auth_info_res, sizeof(send_auth_info_res)); + 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 */ + rc = rx_gsup_message(send_auth_info_err, sizeof(send_auth_info_err)); + OSMO_ASSERT(rc == -GMM_CAUSE_GPRS_NOTALLOWED); + OSMO_ASSERT(last_updated_subscr == s1); + OSMO_ASSERT(s1->sgsn_data->error_cause == GMM_CAUSE_GPRS_NOTALLOWED); + + /* 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 UpdateLocRes GSUP message */ + rc = rx_gsup_message(update_location_res, sizeof(update_location_res)); + OSMO_ASSERT(rc >= 0); + OSMO_ASSERT(last_updated_subscr == s1); + OSMO_ASSERT(s1->flags & GPRS_SUBSCRIBER_ENABLE_PURGE); + OSMO_ASSERT(s1->sgsn_data->error_cause == SGSN_ERROR_CAUSE_NONE); + OSMO_ASSERT(s1->sgsn_data->msisdn_len == sizeof(s1_msisdn)); + OSMO_ASSERT(memcmp(s1->sgsn_data->msisdn, s1_msisdn, sizeof(s1_msisdn)) == 0); + OSMO_ASSERT(!llist_empty(&s1->sgsn_data->pdp_list)); + pdpd = llist_entry(s1->sgsn_data->pdp_list.next, + struct sgsn_subscriber_pdp_data, list); + OSMO_ASSERT(strcmp(pdpd->apn_str, "test.apn") == 0); + pdpd = llist_entry(pdpd->list.next, + struct sgsn_subscriber_pdp_data, list); + OSMO_ASSERT(strcmp(pdpd->apn_str, "foo.apn") == 0); + + /* Check authorization */ + OSMO_ASSERT(s1->authorized == 1); + + /* Inject UpdateLocErr GSUP message */ + rc = rx_gsup_message(update_location_err, sizeof(update_location_err)); + OSMO_ASSERT(rc == -GMM_CAUSE_GPRS_NOTALLOWED); + OSMO_ASSERT(last_updated_subscr == s1); + OSMO_ASSERT(s1->sgsn_data->error_cause == GMM_CAUSE_GPRS_NOTALLOWED); + + /* Check authorization */ + OSMO_ASSERT(s1->authorized == 0); + + /* Inject InsertSubscrData GSUP message */ + last_updated_subscr = NULL; + rc = rx_gsup_message(insert_data_req, sizeof(insert_data_req)); + OSMO_ASSERT(rc == -ENOTSUP); /* not connected */ + OSMO_ASSERT(last_updated_subscr == s1); + + /* Inject DeleteSubscrData GSUP message */ + last_updated_subscr = NULL; + rc = rx_gsup_message(delete_data_req, sizeof(delete_data_req)); + if (rc != -GMM_CAUSE_SEM_INCORR_MSG) + printf("Unexpected response to DSD: %d\n", rc); + OSMO_ASSERT(last_updated_subscr == NULL); + + /* Inject wrong LocCancelReq GSUP message */ + last_updated_subscr = NULL; + rc = rx_gsup_message(location_cancellation_req_other, + sizeof(location_cancellation_req_other)); + OSMO_ASSERT(rc == -GMM_CAUSE_IMSI_UNKNOWN); + OSMO_ASSERT(last_updated_subscr == NULL); + + /* Check cancellation result */ + OSMO_ASSERT(!(s1->flags & GPRS_SUBSCRIBER_CANCELLED)); + OSMO_ASSERT(s1->sgsn_data->mm != NULL); + + /* Inject LocCancelReq GSUP message */ + rc = rx_gsup_message(location_cancellation_req, + sizeof(location_cancellation_req)); + OSMO_ASSERT(rc >= 0); + OSMO_ASSERT(last_updated_subscr == s1); + OSMO_ASSERT(s1->sgsn_data->error_cause == SGSN_ERROR_CAUSE_NONE); + + /* Check cancellation result */ + OSMO_ASSERT(s1->flags & GPRS_SUBSCRIBER_CANCELLED); + OSMO_ASSERT(s1->sgsn_data->mm == NULL); + + /* Inject LocCancelReq(withdraw) GSUP message */ + rc = rx_gsup_message(location_cancellation_req_withdraw, + sizeof(location_cancellation_req_withdraw)); + OSMO_ASSERT(rc >= 0); + OSMO_ASSERT(s1->sgsn_data->error_cause == GMM_CAUSE_IMPL_DETACHED); + + /* Inject PurgeMsRes GSUP message */ + rc = rx_gsup_message(purge_ms_res, + sizeof(purge_ms_res)); + OSMO_ASSERT(rc >= 0); + OSMO_ASSERT(!(s1->flags & GPRS_SUBSCRIBER_ENABLE_PURGE)); + + /* Free MM context and subscriber */ + OSMO_ASSERT(ctx->subscr == NULL); + sgsn_mm_ctx_cleanup_free(ctx); + gprs_subscr_put(s1); + s1found = gprs_subscr_get_by_imsi(imsi1); + OSMO_ASSERT(s1found == NULL); + + /* Inject PurgeMsRes GSUP message */ + rc = rx_gsup_message(purge_ms_res, + sizeof(purge_ms_res)); + OSMO_ASSERT(rc >= 0); + + /* Inject PurgeMsErr(IMSI unknown in HLR) GSUP message */ + rc = rx_gsup_message(purge_ms_err, + sizeof(purge_ms_err)); + OSMO_ASSERT(rc == -GMM_CAUSE_IMSI_UNKNOWN); + + /* Inject PurgeMsErr() GSUP message */ + rc = rx_gsup_message(purge_ms_err_no_cause, + sizeof(purge_ms_err_no_cause)); + OSMO_ASSERT(rc == -GMM_CAUSE_NET_FAIL); + + /* Inject InsertSubscrData GSUP message (unknown IMSI) */ + last_updated_subscr = NULL; + rc = rx_gsup_message(insert_data_req, sizeof(insert_data_req)); + OSMO_ASSERT(rc == -GMM_CAUSE_IMSI_UNKNOWN); + OSMO_ASSERT(last_updated_subscr == NULL); + + /* Inject DeleteSubscrData GSUP message (unknown IMSI) */ + rc = rx_gsup_message(delete_data_req, sizeof(delete_data_req)); + OSMO_ASSERT(rc == -GMM_CAUSE_IMSI_UNKNOWN); + OSMO_ASSERT(last_updated_subscr == NULL); + + /* Inject LocCancelReq GSUP message (unknown IMSI) */ + rc = rx_gsup_message(location_cancellation_req, + sizeof(location_cancellation_req)); + OSMO_ASSERT(rc == -GMM_CAUSE_IMSI_UNKNOWN); + OSMO_ASSERT(last_updated_subscr == NULL); + + update_subscriber_data_cb = __real_sgsn_update_subscriber_data; + + cleanup_test(); +} + +int my_gsup_client_send_dummy(struct gsup_client *gsupc, struct msgb *msg) +{ + msgb_free(msg); + return 0; +}; + +/* + * Test that a GMM Detach will remove the MMCTX and the + * associated LLME. + */ +static void test_gmm_detach(void) +{ + struct gprs_ra_id raid = { 0, }; + struct sgsn_mm_ctx *ctx, *ictx; + uint32_t local_tlli; + + printf("Testing GMM detach\n"); + + /* DTAP - Detach Request (MO) */ + /* normal detach, power_off = 0 */ + static const unsigned char detach_req[] = { + 0x08, 0x05, 0x01, 0x18, 0x05, 0xf4, 0xef, 0xe2, + 0xb7, 0x00, 0x19, 0x03, 0xb9, 0x97, 0xcb + }; + + local_tlli = gprs_tmsi2tlli(0x23, TLLI_LOCAL); + + /* Create a context */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + ctx = alloc_mm_ctx(local_tlli, &raid); + + /* inject the detach */ + send_0408_message(ctx->gb.llme, local_tlli, &raid, + detach_req, ARRAY_SIZE(detach_req)); + + /* verify that a single message (hopefully the Detach Accept) has been + * sent by the SGSN */ + OSMO_ASSERT(sgsn_tx_counter == 1); + + /* verify that things are gone */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + ictx = sgsn_mm_ctx_by_tlli(local_tlli, &raid); + OSMO_ASSERT(!ictx); + + cleanup_test(); +} + +/* + * Test that a GMM Detach will remove the MMCTX and the associated LLME but + * will not sent a Detach Accept message (power_off = 1) + */ +static void test_gmm_detach_power_off(void) +{ + struct gprs_ra_id raid = { 0, }; + struct sgsn_mm_ctx *ctx, *ictx; + uint32_t local_tlli; + + printf("Testing GMM detach (power off)\n"); + + /* DTAP - Detach Request (MO) */ + /* normal detach, power_off = 1 */ + static const unsigned char detach_req[] = { + 0x08, 0x05, 0x09, 0x18, 0x05, 0xf4, 0xef, 0xe2, + 0xb7, 0x00, 0x19, 0x03, 0xb9, 0x97, 0xcb + }; + + local_tlli = gprs_tmsi2tlli(0x23, TLLI_LOCAL); + + /* Create a context */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + ctx = alloc_mm_ctx(local_tlli, &raid); + + /* inject the detach */ + send_0408_message(ctx->gb.llme, local_tlli, &raid, + detach_req, ARRAY_SIZE(detach_req)); + + /* verify that no message (and therefore no Detach Accept) has been + * sent by the SGSN */ + OSMO_ASSERT(sgsn_tx_counter == 0); + + /* verify that things are gone */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + ictx = sgsn_mm_ctx_by_tlli(local_tlli, &raid); + OSMO_ASSERT(!ictx); + + cleanup_test(); +} + +/* + * Test that a GMM Detach will remove the associated LLME if there is no MMCTX. + */ +static void test_gmm_detach_no_mmctx(void) +{ + struct gprs_ra_id raid = { 0, }; + struct gprs_llc_lle *lle; + uint32_t local_tlli; + + printf("Testing GMM detach (no MMCTX)\n"); + + /* DTAP - Detach Request (MO) */ + /* normal detach, power_off = 0 */ + static const unsigned char detach_req[] = { + 0x08, 0x05, 0x01, 0x18, 0x05, 0xf4, 0xef, 0xe2, + 0xb7, 0x00, 0x19, 0x03, 0xb9, 0x97, 0xcb + }; + + /* Create an LLME */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + local_tlli = gprs_tmsi2tlli(0x23, TLLI_LOCAL); + lle = gprs_lle_get_or_create(local_tlli, 3); + + OSMO_ASSERT(count(gprs_llme_list()) == 1); + + /* inject the detach */ + send_0408_message(lle->llme, local_tlli, &raid, + detach_req, ARRAY_SIZE(detach_req)); + + /* verify that the LLME is gone */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + + cleanup_test(); +} + +/* + * Test that a single GMM Detach Accept message will not cause the SGSN to send + * any message or leave an MM context at the SGSN. + */ +static void test_gmm_detach_accept_unexpected(void) +{ + struct gprs_ra_id raid = { 0, }; + struct gprs_llc_lle *lle; + uint32_t local_tlli; + + printf("Testing GMM detach accept (unexpected)\n"); + + /* DTAP - Detach Accept (MT) */ + /* normal detach */ + static const unsigned char detach_acc[] = { + 0x08, 0x06 + }; + + /* Create an LLME */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + local_tlli = gprs_tmsi2tlli(0x23, TLLI_LOCAL); + lle = gprs_lle_get_or_create(local_tlli, 3); + + /* inject the detach */ + send_0408_message(lle->llme, local_tlli, &raid, + detach_acc, ARRAY_SIZE(detach_acc)); + + /* verify that no message (and therefore no Status or XID reset) has been + * sent by the SGSN */ + OSMO_ASSERT(sgsn_tx_counter == 0); + + /* verify that things are gone */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + + cleanup_test(); +} + +/* + * Test that a GMM Status will remove the associated LLME if there is no MMCTX. + */ +static void test_gmm_status_no_mmctx(void) +{ + struct gprs_ra_id raid = { 0, }; + struct gprs_llc_lle *lle; + uint32_t local_tlli; + + printf("Testing GMM Status (no MMCTX)\n"); + + /* DTAP - GMM Status, protocol error */ + static const unsigned char gmm_status[] = { + 0x08, 0x20, 0x6f + }; + + /* Create an LLME */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + local_tlli = gprs_tmsi2tlli(0x23, TLLI_LOCAL); + lle = gprs_lle_get_or_create(local_tlli, 3); + + OSMO_ASSERT(count(gprs_llme_list()) == 1); + + /* inject the detach */ + send_0408_message(lle->llme, local_tlli, &raid, + gmm_status, ARRAY_SIZE(gmm_status)); + + /* verify that no message has been sent by the SGSN */ + OSMO_ASSERT(sgsn_tx_counter == 0); + + /* verify that the LLME is gone */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + + cleanup_test(); +} + +/* + * Test the GMM Attach procedure + */ +static void test_gmm_attach(int retry) +{ + struct gprs_ra_id raid = { 0, }; + struct sgsn_mm_ctx *ctx = NULL; + struct sgsn_mm_ctx *ictx; + uint32_t ptmsi1; + uint32_t foreign_tlli; + uint32_t local_tlli = 0; + struct gprs_llc_lle *lle; + + /* DTAP - Attach Request */ + /* The P-TMSI is not known by the SGSN */ + static const unsigned char attach_req[] = { + 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x05, 0xf4, + 0xfb, 0xc5, 0x46, 0x79, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, + 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, + 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, + 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00 + }; + + /* DTAP - Identity Response IMEI */ + static const unsigned char ident_resp_imei[] = { + 0x08, 0x16, 0x08, 0x9a, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, + 0x56 + }; + + /* DTAP - Identity Response IMSI */ + static const unsigned char ident_resp_imsi[] = { + 0x08, 0x16, 0x08, 0x19, 0x32, 0x54, 0x76, 0x98, 0x10, 0x32, + 0x54 + }; + + /* DTAP - Authentication and Ciphering Resp */ + static const unsigned char auth_ciph_resp[] = { + 0x08, 0x13, 0x00, 0x22, 0x51, 0xe5, 0x51, 0xe5, 0x23, 0x09, + 0x9a, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x01 + }; + + /* DTAP - Attach Complete */ + static const unsigned char attach_compl[] = { + 0x08, 0x03 + }; + + /* DTAP - Detach Request (MO) */ + /* normal detach, power_off = 0 */ + static const unsigned char detach_req[] = { + 0x08, 0x05, 0x01, 0x18, 0x05, 0xf4, 0xeb, 0x8b, + 0x45, 0x67, 0x19, 0x03, 0xb9, 0x97, 0xcb + }; + + printf("Testing GMM attach%s\n", retry ? " with retry" : ""); + + foreign_tlli = gprs_tmsi2tlli(0xc0000023, TLLI_FOREIGN); + + /* Create a LLE/LLME */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + lle = gprs_lle_get_or_create(foreign_tlli, 3); + OSMO_ASSERT(count(gprs_llme_list()) == 1); + + /* inject the attach request */ + send_0408_message(lle->llme, foreign_tlli, &raid, + attach_req, ARRAY_SIZE(attach_req)); + + ctx = sgsn_mm_ctx_by_tlli(foreign_tlli, &raid); + OSMO_ASSERT(ctx != NULL); + OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); + + /* we expect an identity request (IMEI) */ + OSMO_ASSERT(sgsn_tx_counter == 1); + + /* inject the identity response (IMEI) */ + send_0408_message(ctx->gb.llme, foreign_tlli, &raid, + ident_resp_imei, ARRAY_SIZE(ident_resp_imei)); + + /* we expect an identity request (IMSI) */ + OSMO_ASSERT(sgsn_tx_counter == 1); + + /* inject the identity response (IMSI) */ + send_0408_message(ctx->gb.llme, foreign_tlli, &raid, + ident_resp_imsi, ARRAY_SIZE(ident_resp_imsi)); + + /* check that the MM context has not been removed due to a failed + * authorization */ + OSMO_ASSERT(ctx == sgsn_mm_ctx_by_tlli(foreign_tlli, &raid)); + + OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); + +retry_attach_req: + + if (retry && sgsn_tx_counter == 0) { + fprintf(stderr, "Retrying attach request\n"); + /* re-inject the attach request */ + send_0408_message(lle->llme, foreign_tlli, &raid, + attach_req, ARRAY_SIZE(attach_req)); + } + + if (ctx->auth_state == SGSN_AUTH_AUTHENTICATE && sgsn_tx_counter == 1) { + /* we got an auth & ciph request */ + + /* inject the auth & ciph response */ + send_0408_message(ctx->gb.llme, foreign_tlli, &raid, + auth_ciph_resp, ARRAY_SIZE(auth_ciph_resp)); + + /* check that the MM context has not been removed due to a + * failed authorization */ + OSMO_ASSERT(ctx == sgsn_mm_ctx_by_tlli(foreign_tlli, &raid)); + if (ctx->subscr && ctx->subscr->sgsn_data->msisdn_len > 0) + OSMO_ASSERT(strcmp(ctx->msisdn, "+49166213323") == 0); + } + + if (retry && sgsn_tx_counter == 0) + goto retry_attach_req; + + /* we expect an attach accept/reject */ + OSMO_ASSERT(sgsn_tx_counter == 1); + ptmsi1 = get_new_ptmsi(&last_dl_parse_ctx); + OSMO_ASSERT(ptmsi1 != GSM_RESERVED_TMSI); + + /* this has been randomly assigned by the SGSN */ + local_tlli = gprs_tmsi2tlli(ptmsi1, TLLI_LOCAL); + + /* inject the attach complete */ + send_0408_message(ctx->gb.llme, local_tlli, &raid, + attach_compl, ARRAY_SIZE(attach_compl)); + + OSMO_ASSERT(ctx->gmm_state == GMM_REGISTERED_NORMAL); + + /* we don't expect a response */ + OSMO_ASSERT(sgsn_tx_counter == 0); + + /* inject the detach */ + send_0408_message(ctx->gb.llme, local_tlli, &raid, + detach_req, ARRAY_SIZE(detach_req)); + + /* verify that things are gone */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + ictx = sgsn_mm_ctx_by_tlli(local_tlli, &raid); + OSMO_ASSERT(!ictx); + + cleanup_test(); +} + +static void test_gmm_attach_acl(void) +{ + const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; + + sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_CLOSED; + sgsn_acl_add("123456789012345", &sgsn->cfg); + printf("Auth policy 'closed': "); + test_gmm_attach(0); + sgsn_acl_del("123456789012345", &sgsn->cfg); + + sgsn->cfg.auth_policy = saved_auth_policy; + + cleanup_test(); +} + +int my_subscr_request_update_location(struct sgsn_mm_ctx *mmctx) { + int rc; + rc = __real_gprs_subscr_request_update_location(mmctx); + if (rc == -ENOTSUP) { + OSMO_ASSERT(mmctx->subscr); + gprs_subscr_update(mmctx->subscr); + } + return rc; +}; + +int my_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx) { + gprs_subscr_update(mmctx->subscr); + return 0; +}; + +static void test_gmm_attach_subscr(void) +{ + const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; + struct gprs_subscr *subscr; + + sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_REMOTE; + subscr_request_update_location_cb = my_subscr_request_update_location; + subscr_request_auth_info_cb = my_subscr_request_auth_info; + + subscr = gprs_subscr_get_or_create("123456789012345"); + subscr->authorized = 1; + + printf("Auth policy 'remote': "); + test_gmm_attach(0); + gprs_subscr_put(subscr); + assert_no_subscrs(); + + sgsn->cfg.auth_policy = saved_auth_policy; + subscr_request_update_location_cb = __real_gprs_subscr_request_update_location; + subscr_request_auth_info_cb = __real_gprs_subscr_request_auth_info; + + cleanup_test(); +} + +int my_subscr_request_auth_info_fake_auth(struct sgsn_mm_ctx *mmctx) +{ + /* Fake an authentication */ + OSMO_ASSERT(mmctx->subscr); + mmctx->is_authenticated = 1; + gprs_subscr_update_auth_info(mmctx->subscr); + + return 0; +}; + +static void test_gmm_attach_subscr_fake_auth(void) +{ + const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; + struct gprs_subscr *subscr; + + sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_REMOTE; + subscr_request_update_location_cb = my_subscr_request_update_location; + subscr_request_auth_info_cb = my_subscr_request_auth_info_fake_auth; + + subscr = gprs_subscr_get_or_create("123456789012345"); + subscr->authorized = 1; + sgsn->cfg.require_authentication = 1; + sgsn->cfg.require_update_location = 1; + + printf("Auth policy 'remote', auth faked: "); + test_gmm_attach(0); + gprs_subscr_put(subscr); + assert_no_subscrs(); + + sgsn->cfg.auth_policy = saved_auth_policy; + subscr_request_update_location_cb = __real_gprs_subscr_request_update_location; + subscr_request_auth_info_cb = __real_gprs_subscr_request_auth_info; + + cleanup_test(); +} + +int my_subscr_request_auth_info_real_auth(struct sgsn_mm_ctx *mmctx) +{ + struct gsm_auth_tuple at = { + .vec.sres = {0x51, 0xe5, 0x51, 0xe5}, + .vec.auth_types = OSMO_AUTH_TYPE_GSM, + .key_seq = 0 + }; + + /* Fake an authentication */ + OSMO_ASSERT(mmctx->subscr); + mmctx->subscr->sgsn_data->auth_triplets[0] = at; + + gprs_subscr_update_auth_info(mmctx->subscr); + + return 0; +}; + +static void test_gmm_attach_subscr_real_auth(void) +{ + const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; + struct gprs_subscr *subscr; + + sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_REMOTE; + subscr_request_update_location_cb = my_subscr_request_update_location; + subscr_request_auth_info_cb = my_subscr_request_auth_info_real_auth; + + subscr = gprs_subscr_get_or_create("123456789012345"); + subscr->authorized = 1; + sgsn->cfg.require_authentication = 1; + sgsn->cfg.require_update_location = 1; + + printf("Auth policy 'remote', triplet based auth: "); + + test_gmm_attach(0); + gprs_subscr_put(subscr); + assert_no_subscrs(); + + sgsn->cfg.auth_policy = saved_auth_policy; + subscr_request_update_location_cb = __real_gprs_subscr_request_update_location; + subscr_request_auth_info_cb = __real_gprs_subscr_request_auth_info; + + cleanup_test(); +} + +#define TEST_GSUP_IMSI_LONG_IE 0x01, 0x08, \ + 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0xf5 + +static int auth_info_skip = 0; +static int upd_loc_skip = 0; + +int my_subscr_request_auth_info_gsup_auth(struct sgsn_mm_ctx *mmctx) +{ + static const uint8_t send_auth_info_res[] = { + 0x0a, + TEST_GSUP_IMSI_LONG_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, + 0x51, 0xe5, 0x51, 0xe5, + 0x22, 0x08, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + }; + + OSMO_ASSERT(!mmctx || mmctx->subscr); + + if (auth_info_skip > 0) { + auth_info_skip -= 1; + return -EAGAIN; + } + + /* Fake an SendAuthInfoRes */ + rx_gsup_message(send_auth_info_res, sizeof(send_auth_info_res)); + + return 0; +}; + +int my_subscr_request_update_gsup_auth(struct sgsn_mm_ctx *mmctx) { + static const uint8_t update_location_res[] = { + 0x06, + TEST_GSUP_IMSI_LONG_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', + 0x08, 0x07, /* MSISDN 49166213323 encoded */ + 0x91, 0x94, 0x61, 0x26, 0x31, 0x23, 0xF3, + 0x09, 0x07, /* MSISDN 38166213323 encoded */ + 0x91, 0x83, 0x61, 0x26, 0x31, 0x23, 0xF3, + }; + + OSMO_ASSERT(!mmctx || mmctx->subscr); + + if (upd_loc_skip > 0) { + upd_loc_skip -= 1; + return -EAGAIN; + } + + /* Fake an UpdateLocRes */ + return rx_gsup_message(update_location_res, sizeof(update_location_res)); +}; + + +static void test_gmm_attach_subscr_gsup_auth(int retry) +{ + const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; + struct gprs_subscr *subscr; + + sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_REMOTE; + subscr_request_update_location_cb = my_subscr_request_update_gsup_auth; + subscr_request_auth_info_cb = my_subscr_request_auth_info_gsup_auth; + if (retry) { + upd_loc_skip = 3; + auth_info_skip = 3; + } + + subscr = gprs_subscr_get_or_create("123456789012345"); + subscr->authorized = 1; + sgsn->cfg.require_authentication = 1; + sgsn->cfg.require_update_location = 1; + gprs_subscr_put(subscr); + + printf("Auth policy 'remote', GSUP based auth: "); + test_gmm_attach(retry); + assert_no_subscrs(); + + sgsn->cfg.auth_policy = saved_auth_policy; + subscr_request_update_location_cb = __real_gprs_subscr_request_update_location; + subscr_request_auth_info_cb = __real_gprs_subscr_request_auth_info; + upd_loc_skip = 0; + auth_info_skip = 0; + + cleanup_test(); +} + +int my_gsup_client_send(struct gsup_client *gsupc, struct msgb *msg) +{ + struct osmo_gsup_message to_peer = {0}; + struct osmo_gsup_message from_peer = {0}; + struct msgb *reply_msg; + int rc; + + /* Simulate the GSUP peer */ + rc = osmo_gsup_decode(msgb_data(msg), msgb_length(msg), &to_peer); + OSMO_ASSERT(rc >= 0); + OSMO_ASSERT(to_peer.imsi[0] != 0); + osmo_strlcpy(from_peer.imsi, to_peer.imsi, sizeof(from_peer.imsi)); + + /* This invalidates the pointers in to_peer */ + msgb_free(msg); + + switch (to_peer.message_type) { + case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST: + /* Send UPDATE_LOCATION_RESULT */ + return my_subscr_request_update_gsup_auth(NULL); + + case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST: + /* Send SEND_AUTH_INFO_RESULT */ + return my_subscr_request_auth_info_gsup_auth(NULL); + + case OSMO_GSUP_MSGT_PURGE_MS_REQUEST: + from_peer.message_type = OSMO_GSUP_MSGT_PURGE_MS_RESULT; + break; + + default: + if ((to_peer.message_type & 0b00000011) == 0) { + /* Unhandled request */ + /* Send error(NOT_IMPL) */ + from_peer.message_type = to_peer.message_type + 1; + from_peer.cause = GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL; + break; + } + + /* Ignore it */ + return 0; + } + + reply_msg = gsup_client_msgb_alloc(); + reply_msg->l2h = reply_msg->data; + osmo_gsup_encode(reply_msg, &from_peer); + gprs_subscr_rx_gsup_message(reply_msg); + msgb_free(reply_msg); + + return 0; +}; + +static void test_gmm_attach_subscr_real_gsup_auth(int retry) +{ + const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; + struct gprs_subscr *subscr; + + sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_REMOTE; + gsup_client_send_cb = my_gsup_client_send; + + sgsn->gsup_client = talloc_zero(tall_bsc_ctx, struct gsup_client); + + if (retry) { + upd_loc_skip = 3; + auth_info_skip = 3; + } + + printf("Auth policy 'remote', real GSUP based auth: "); + test_gmm_attach(retry); + + subscr = gprs_subscr_get_by_imsi("123456789012345"); + OSMO_ASSERT(subscr == NULL); + assert_no_subscrs(); + + sgsn->cfg.auth_policy = saved_auth_policy; + gsup_client_send_cb = __real_gsup_client_send; + upd_loc_skip = 0; + auth_info_skip = 0; + talloc_free(sgsn->gsup_client); + sgsn->gsup_client = NULL; + + cleanup_test(); +} + +/* + * Test the GMM Rejects + */ +static void test_gmm_reject(void) +{ + struct gprs_ra_id raid = { 0, }; + struct sgsn_mm_ctx *ctx = NULL; + uint32_t foreign_tlli; + struct gprs_llc_lle *lle; + int idx; + + /* DTAP - Attach Request */ + /* Invalid MI length */ + static const unsigned char attach_req_inv_mi_len[] = { + 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x09, 0xf4, + 0xfb, 0xc5, 0x46, 0x79, 0xff, 0xff, 0xff, 0xff, 0x11, 0x22, + 0x33, 0x40, 0x50, 0x60, 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, + 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, + 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00 + }; + + /* DTAP - Attach Request */ + /* Invalid MI type (IMEI) */ + static const unsigned char attach_req_inv_mi_type[] = { + 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x05, 0xf2, + 0xfb, 0xc5, 0x46, 0x79, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, + 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, + 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, + 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00 + }; + + /* DTAP - Routing Area Update Request */ + static const unsigned char dtap_ra_upd_req[] = { + 0x08, 0x08, 0x10, 0x11, 0x22, 0x33, 0x40, 0x50, + 0x60, 0x1d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b, + 0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8, + 0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02, + 0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19, + 0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04, + 0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00 + }; + + /* DTAP - Routing Area Update Request */ + /* Invalid type: GPRS_UPD_T_RA_LA_IMSI_ATT */ + static const unsigned char dtap_ra_upd_req_inv_type[] = { + 0x08, 0x08, 0x12, 0x11, 0x22, 0x33, 0x40, 0x50, + 0x60, 0x1d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b, + 0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8, + 0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02, + 0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19, + 0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04, + 0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00 + }; + + /* DTAP - Routing Area Update Request */ + /* Invalid cap length */ + static const unsigned char dtap_ra_upd_req_inv_cap_len[] = { + 0x08, 0x08, 0x10, 0x11, 0x22, 0x33, 0x40, 0x50, + 0x60, 0x3d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8, + 0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02, + 0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19, + 0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04, + 0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00 + }; + + struct test { + const char *title; + const unsigned char *msg; + unsigned msg_len; + unsigned num_resp; + + }; + static struct test tests[] = { + { + .title = "Attach Request (invalid MI length)", + .msg = attach_req_inv_mi_len, + .msg_len = sizeof(attach_req_inv_mi_len), + .num_resp = 1 /* Reject */ + + }, + { + .title = "Attach Request (invalid MI type)", + .msg = attach_req_inv_mi_type, + .msg_len = sizeof(attach_req_inv_mi_type), + .num_resp = 1 /* Reject */ + }, + { + .title = "Routing Area Update Request (valid)", + .msg = dtap_ra_upd_req, + .msg_len = sizeof(dtap_ra_upd_req), + .num_resp = 2 /* XID Reset + Reject */ + }, + { + .title = "Routing Area Update Request (invalid type)", + .msg = dtap_ra_upd_req_inv_type, + .msg_len = sizeof(dtap_ra_upd_req_inv_type), + .num_resp = 1 /* Reject */ + }, + { + .title = "Routing Area Update Request (invalid CAP length)", + .msg = dtap_ra_upd_req_inv_cap_len, + .msg_len = sizeof(dtap_ra_upd_req_inv_cap_len), + .num_resp = 1 /* Reject */ + }, + }; + + printf("Testing GMM reject\n"); + + /* reset the PRNG used by sgsn_alloc_ptmsi */ + srand(1); + + foreign_tlli = gprs_tmsi2tlli(0xc0000023, TLLI_FOREIGN); + + OSMO_ASSERT(count(gprs_llme_list()) == 0); + + for (idx = 0; idx < ARRAY_SIZE(tests); idx++) { + const struct test *test = &tests[idx]; + printf(" - %s\n", test->title); + + /* Create a LLE/LLME */ + lle = gprs_lle_get_or_create(foreign_tlli, 3); + OSMO_ASSERT(count(gprs_llme_list()) == 1); + + /* Inject the Request message */ + send_0408_message(lle->llme, foreign_tlli, &raid, + test->msg, test->msg_len); + + /* We expect a Reject message */ + fprintf(stderr, "sgsn_tx_counter = %d (expected %d)\n", + sgsn_tx_counter, test->num_resp); + OSMO_ASSERT(sgsn_tx_counter == test->num_resp); + + /* verify that LLME/MM are removed */ + ctx = sgsn_mm_ctx_by_tlli(foreign_tlli, &raid); + OSMO_ASSERT(ctx == NULL); + OSMO_ASSERT(count(gprs_llme_list()) == 0); + } + + cleanup_test(); +} + +/* + * Test cancellation of attached MM contexts + */ +static void test_gmm_cancel(void) +{ + struct gprs_ra_id raid = { 0, }; + struct sgsn_mm_ctx *ctx = NULL; + struct sgsn_mm_ctx *ictx; + uint32_t ptmsi1; + uint32_t foreign_tlli; + uint32_t local_tlli = 0; + struct gprs_llc_lle *lle; + const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; + + /* DTAP - Attach Request */ + /* The P-TMSI is not known by the SGSN */ + static const unsigned char attach_req[] = { + 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x05, 0xf4, + 0xfb, 0xc5, 0x46, 0x79, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, + 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, + 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, + 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00 + }; + + /* DTAP - Identity Response IMEI */ + static const unsigned char ident_resp_imei[] = { + 0x08, 0x16, 0x08, 0x9a, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, + 0x56 + }; + + /* DTAP - Identity Response IMSI */ + static const unsigned char ident_resp_imsi[] = { + 0x08, 0x16, 0x08, 0x19, 0x32, 0x54, 0x76, 0x98, 0x10, 0x32, + 0x54 + }; + + /* DTAP - Attach Complete */ + static const unsigned char attach_compl[] = { + 0x08, 0x03 + }; + + printf("Testing cancellation\n"); + + sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_OPEN; + + foreign_tlli = gprs_tmsi2tlli(0xc0000023, TLLI_FOREIGN); + + /* Create a LLE/LLME */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + lle = gprs_lle_get_or_create(foreign_tlli, 3); + OSMO_ASSERT(count(gprs_llme_list()) == 1); + + /* inject the attach request */ + send_0408_message(lle->llme, foreign_tlli, &raid, + attach_req, ARRAY_SIZE(attach_req)); + + ctx = sgsn_mm_ctx_by_tlli(foreign_tlli, &raid); + OSMO_ASSERT(ctx != NULL); + OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); + + /* we expect an identity request (IMEI) */ + OSMO_ASSERT(sgsn_tx_counter == 1); + + /* inject the identity response (IMEI) */ + send_0408_message(ctx->gb.llme, foreign_tlli, &raid, + ident_resp_imei, ARRAY_SIZE(ident_resp_imei)); + + /* we expect an identity request (IMSI) */ + OSMO_ASSERT(sgsn_tx_counter == 1); + + /* inject the identity response (IMSI) */ + send_0408_message(ctx->gb.llme, foreign_tlli, &raid, + ident_resp_imsi, ARRAY_SIZE(ident_resp_imsi)); + + /* check that the MM context has not been removed due to a failed + * authorization */ + OSMO_ASSERT(ctx == sgsn_mm_ctx_by_tlli(foreign_tlli, &raid)); + + OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); + + /* we expect an attach accept/reject */ + OSMO_ASSERT(sgsn_tx_counter == 1); + ptmsi1 = get_new_ptmsi(&last_dl_parse_ctx); + OSMO_ASSERT(ptmsi1 != GSM_RESERVED_TMSI); + + /* this has been randomly assigned by the SGSN */ + local_tlli = gprs_tmsi2tlli(ptmsi1, TLLI_LOCAL); + + /* inject the attach complete */ + send_0408_message(ctx->gb.llme, foreign_tlli, &raid, + attach_compl, ARRAY_SIZE(attach_compl)); + + OSMO_ASSERT(ctx->gmm_state == GMM_REGISTERED_NORMAL); + + /* we don't expect a response */ + OSMO_ASSERT(sgsn_tx_counter == 0); + + /* cancel */ + gsm0408_gprs_access_cancelled(ctx, 0); + + /* verify that things are gone */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + ictx = sgsn_mm_ctx_by_tlli(local_tlli, &raid); + OSMO_ASSERT(!ictx); + + sgsn->cfg.auth_policy = saved_auth_policy; + + cleanup_test(); +} + +/* + * Test the dynamic allocation of P-TMSIs + */ +static void test_gmm_ptmsi_allocation(void) +{ + struct gprs_ra_id raid = {332, 112, 16464, 96}; + struct sgsn_mm_ctx *ctx = NULL; + struct sgsn_mm_ctx *ictx; + uint32_t foreign_tlli; + uint32_t ptmsi1; + uint32_t ptmsi2; + uint32_t received_ptmsi; + uint32_t old_ptmsi; + uint32_t local_tlli = 0; + struct gprs_llc_lle *lle; + const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; + + /* DTAP - Attach Request (IMSI 12131415161718) */ + static const unsigned char attach_req[] = { + 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, + 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x19, + 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, + 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, + 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, + 0x00, + }; + + /* DTAP - Identity Response IMEI */ + static const unsigned char ident_resp_imei[] = { + 0x08, 0x16, 0x08, 0x9a, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, + 0x56 + }; + + /* DTAP - Attach Complete */ + static const unsigned char attach_compl[] = { + 0x08, 0x03 + }; + + /* DTAP - Routing Area Update Request */ + static const unsigned char ra_upd_req[] = { + 0x08, 0x08, 0x10, 0x11, 0x22, 0x33, 0x40, 0x50, + 0x60, 0x1d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b, + 0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8, + 0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02, + 0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19, + 0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04, + 0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00 + }; + + /* DTAP - Routing Area Update Complete */ + static const unsigned char ra_upd_complete[] = { + 0x08, 0x0a + }; + + /* DTAP - Detach Request (MO) */ + /* normal detach, power_off = 1 */ + static const unsigned char detach_req[] = { + 0x08, 0x05, 0x09, 0x18, 0x05, 0xf4, 0xef, 0xe2, + 0xb7, 0x00, 0x19, 0x03, 0xb9, 0x97, 0xcb + }; + + sgsn->cfg.auth_policy = SGSN_AUTH_POLICY_OPEN; + + printf("Testing P-TMSI allocation\n"); + + printf(" - sgsn_alloc_ptmsi\n"); + + /* reset the PRNG used by sgsn_alloc_ptmsi */ + srand(1); + + ptmsi1 = sgsn_alloc_ptmsi(); + OSMO_ASSERT(ptmsi1 != GSM_RESERVED_TMSI); + + ptmsi2 = sgsn_alloc_ptmsi(); + OSMO_ASSERT(ptmsi2 != GSM_RESERVED_TMSI); + + OSMO_ASSERT(ptmsi1 != ptmsi2); + + ptmsi1 = ptmsi2 = GSM_RESERVED_TMSI; + + printf(" - Repeated Attach Request\n"); + + foreign_tlli = gprs_tmsi2tlli(0xc0000023, TLLI_FOREIGN); + + /* Create a LLE/LLME */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + lle = gprs_lle_get_or_create(foreign_tlli, 3); + OSMO_ASSERT(count(gprs_llme_list()) == 1); + + /* inject the attach request */ + send_0408_message(lle->llme, foreign_tlli, &raid, + attach_req, ARRAY_SIZE(attach_req)); + + ctx = sgsn_mm_ctx_by_tlli(foreign_tlli, &raid); + OSMO_ASSERT(ctx != NULL); + OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); + OSMO_ASSERT(ctx->p_tmsi != GSM_RESERVED_TMSI); + ptmsi1 = ctx->p_tmsi; + + old_ptmsi = ctx->p_tmsi_old; + + /* we expect an identity request (IMEI) */ + OSMO_ASSERT(sgsn_tx_counter == 1); + + /* inject the identity response (IMEI) */ + send_0408_message(ctx->gb.llme, foreign_tlli, &raid, + ident_resp_imei, ARRAY_SIZE(ident_resp_imei)); + + /* check that the MM context has not been removed due to a failed + * authorization */ + OSMO_ASSERT(ctx == sgsn_mm_ctx_by_tlli(foreign_tlli, &raid)); + + OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); + OSMO_ASSERT(ctx->p_tmsi == ptmsi1); + + /* we expect an attach accept */ + OSMO_ASSERT(sgsn_tx_counter == 1); + received_ptmsi = get_new_ptmsi(&last_dl_parse_ctx); + OSMO_ASSERT(received_ptmsi == ptmsi1); + + /* we ignore this and send the attach again */ + send_0408_message(lle->llme, foreign_tlli, &raid, + attach_req, ARRAY_SIZE(attach_req)); + + /* the allocated P-TMSI should be the same */ + ctx = sgsn_mm_ctx_by_tlli(foreign_tlli, &raid); + OSMO_ASSERT(ctx != NULL); + OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); + OSMO_ASSERT(ctx->p_tmsi_old == old_ptmsi); + OSMO_ASSERT(ctx->p_tmsi == ptmsi1); + + /* we expect an attach accept */ + OSMO_ASSERT(sgsn_tx_counter == 1); + received_ptmsi = get_new_ptmsi(&last_dl_parse_ctx); + OSMO_ASSERT(received_ptmsi == ptmsi1); + + /* inject the attach complete */ + local_tlli = gprs_tmsi2tlli(ptmsi1, TLLI_LOCAL); + send_0408_message(ctx->gb.llme, local_tlli, &raid, + attach_compl, ARRAY_SIZE(attach_compl)); + + /* we don't expect a response */ + OSMO_ASSERT(sgsn_tx_counter == 0); + + OSMO_ASSERT(ctx->gmm_state == GMM_REGISTERED_NORMAL); + OSMO_ASSERT(ctx->p_tmsi_old == 0); + OSMO_ASSERT(ctx->p_tmsi == ptmsi1); + + printf(" - Repeated RA Update Request\n"); + + /* inject the RA update request */ + send_0408_message(ctx->gb.llme, local_tlli, &raid, + ra_upd_req, ARRAY_SIZE(ra_upd_req)); + + /* we expect an RA update accept */ + OSMO_ASSERT(sgsn_tx_counter == 1); + + OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); + OSMO_ASSERT(ctx->p_tmsi_old == ptmsi1); + OSMO_ASSERT(ctx->p_tmsi != GSM_RESERVED_TMSI); + OSMO_ASSERT(ctx->p_tmsi != ptmsi1); + ptmsi2 = ctx->p_tmsi; + + /* repeat the RA update request */ + send_0408_message(ctx->gb.llme, local_tlli, &raid, + ra_upd_req, ARRAY_SIZE(ra_upd_req)); + + /* we expect an RA update accept */ + OSMO_ASSERT(sgsn_tx_counter == 1); + received_ptmsi = get_new_ptmsi(&last_dl_parse_ctx); + OSMO_ASSERT(received_ptmsi == ptmsi2); + + OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); + OSMO_ASSERT(ctx->p_tmsi_old == ptmsi1); + OSMO_ASSERT(ctx->p_tmsi == ptmsi2); + + /* inject the RA update complete */ + local_tlli = gprs_tmsi2tlli(ptmsi2, TLLI_LOCAL); + send_0408_message(ctx->gb.llme, local_tlli, &raid, + ra_upd_complete, ARRAY_SIZE(ra_upd_complete)); + + /* we don't expect a response */ + OSMO_ASSERT(sgsn_tx_counter == 0); + + OSMO_ASSERT(ctx->gmm_state == GMM_REGISTERED_NORMAL); + OSMO_ASSERT(ctx->p_tmsi_old == 0); + OSMO_ASSERT(ctx->p_tmsi == ptmsi2); + + /* inject the detach */ + send_0408_message(ctx->gb.llme, local_tlli, &raid, + detach_req, ARRAY_SIZE(detach_req)); + + /* verify that things are gone */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + ictx = sgsn_mm_ctx_by_tlli(local_tlli, &raid); + OSMO_ASSERT(!ictx); + + sgsn->cfg.auth_policy = saved_auth_policy; + + cleanup_test(); +} + +/* + * Test changing of routing areas + */ +static void test_gmm_routing_areas(void) +{ + struct gprs_ra_id raid1 = {332, 112, 16464, 96}; + struct gprs_ra_id raid2 = {332, 112, 16464, 97}; + struct sgsn_mm_ctx *ctx = NULL; + struct sgsn_mm_ctx *ictx; + uint32_t ptmsi1; + uint32_t received_ptmsi; + uint32_t ms_tlli = 0; + struct gprs_llc_lle *lle; + const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; + + /* DTAP - Attach Request (IMSI 12131415161718) */ + static const unsigned char attach_req[] = { + 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, + 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x19, + 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, + 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, + 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, + 0x00, + }; + + /* DTAP - Attach Request (IMSI 12131415161718) (RA 2) */ + static const unsigned char attach_req2[] = { + 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, + 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x11, 0x22, 0x33, 0x40, 0x50, 0x61, 0x19, + 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, + 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, + 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, + 0x00, + }; + + /* DTAP - Identity Response IMEI */ + static const unsigned char ident_resp_imei[] = { + 0x08, 0x16, 0x08, 0x9a, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, + 0x56 + }; + + /* DTAP - Attach Complete */ + static const unsigned char attach_compl[] = { + 0x08, 0x03 + }; + + /* DTAP - Routing Area Update Request (coming from RA 1) */ + static const unsigned char ra_upd_req1[] = { + 0x08, 0x08, 0x10, 0x11, 0x22, 0x33, 0x40, 0x50, + 0x60, 0x1d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b, + 0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8, + 0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02, + 0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19, + 0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04, + 0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00 + }; + + /* DTAP - Routing Area Update Request (coming from RA 2) */ + static const unsigned char ra_upd_req2[] = { + 0x08, 0x08, 0x10, 0x11, 0x22, 0x33, 0x40, 0x50, + 0x61, 0x1d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b, + 0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8, + 0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02, + 0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19, + 0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04, + 0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00 + }; + + /* DTAP - Routing Area Update Request (coming from RA other) */ + /* raid_other = {443, 223, 16464, 98}; */ + static const unsigned char ra_upd_req_other[] = { + 0x08, 0x08, 0x10, 0x22, 0x33, 0x44, 0x40, 0x50, + 0x62, 0x1d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b, + 0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8, + 0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02, + 0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19, + 0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04, + 0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00 + }; + + /* DTAP - Routing Area Update Complete */ + static const unsigned char ra_upd_complete[] = { + 0x08, 0x0a + }; + + /* DTAP - Detach Request (MO) */ + /* normal detach, power_off = 1 */ + static const unsigned char detach_req[] = { + 0x08, 0x05, 0x09, 0x18, 0x05, 0xf4, 0xef, 0xe2, + 0xb7, 0x00, 0x19, 0x03, 0xb9, 0x97, 0xcb + }; + + sgsn->cfg.auth_policy = SGSN_AUTH_POLICY_OPEN; + + printf("Testing routing area changes\n"); + + /* reset the PRNG used by sgsn_alloc_ptmsi */ + srand(1); + + ptmsi1 = GSM_RESERVED_TMSI; + + printf(" - Attach Request (RA 1)\n"); + + ms_tlli = gprs_tmsi2tlli(0x00000023, TLLI_RANDOM); + + /* Create a LLE/LLME */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + lle = gprs_lle_get_or_create(ms_tlli, 3); + OSMO_ASSERT(count(gprs_llme_list()) == 1); + + /* inject the attach request */ + send_0408_message(lle->llme, ms_tlli, &raid1, + attach_req, ARRAY_SIZE(attach_req)); + + ctx = sgsn_mm_ctx_by_tlli(ms_tlli, &raid1); + OSMO_ASSERT(ctx != NULL); + OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); + OSMO_ASSERT(ctx->p_tmsi != GSM_RESERVED_TMSI); + + /* we expect an identity request (IMEI) */ + OSMO_ASSERT(sgsn_tx_counter == 1); + OSMO_ASSERT(last_dl_parse_ctx.g48_hdr->msg_type == GSM48_MT_GMM_ID_REQ); + OSMO_ASSERT(last_dl_parse_ctx.tlli == ms_tlli); + + /* inject the identity response (IMEI) */ + send_0408_message(ctx->gb.llme, ms_tlli, &raid1, + ident_resp_imei, ARRAY_SIZE(ident_resp_imei)); + + /* check that the MM context has not been removed due to a failed + * authorization */ + OSMO_ASSERT(ctx == sgsn_mm_ctx_by_tlli(ms_tlli, &raid1)); + + OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); + + /* we expect an attach accept */ + OSMO_ASSERT(sgsn_tx_counter == 1); + OSMO_ASSERT(last_dl_parse_ctx.g48_hdr->msg_type == GSM48_MT_GMM_ATTACH_ACK); + OSMO_ASSERT(last_dl_parse_ctx.tlli == ms_tlli); + + received_ptmsi = get_new_ptmsi(&last_dl_parse_ctx); + OSMO_ASSERT(received_ptmsi == ctx->p_tmsi); + ptmsi1 = received_ptmsi; + + /* inject the attach complete */ + ms_tlli = gprs_tmsi2tlli(ptmsi1, TLLI_LOCAL); + send_0408_message(ctx->gb.llme, ms_tlli, &raid1, + attach_compl, ARRAY_SIZE(attach_compl)); + + /* we don't expect a response */ + OSMO_ASSERT(sgsn_tx_counter == 0); + + OSMO_ASSERT(ctx->gmm_state == GMM_REGISTERED_NORMAL); + OSMO_ASSERT(ctx->p_tmsi_old == 0); + OSMO_ASSERT(ctx->p_tmsi == ptmsi1); + + printf(" - RA Update Request (RA 1 -> RA 1)\n"); + + /* inject the RA update request */ + send_0408_message(ctx->gb.llme, ms_tlli, &raid1, + ra_upd_req1, ARRAY_SIZE(ra_upd_req1)); + + /* we expect an RA update accept */ + OSMO_ASSERT(sgsn_tx_counter == 1); + OSMO_ASSERT(last_dl_parse_ctx.g48_hdr->msg_type == GSM48_MT_GMM_RA_UPD_ACK); + // OSMO_ASSERT(last_dl_parse_ctx.tlli == ms_tlli); + + OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); + OSMO_ASSERT(ctx->p_tmsi_old == ptmsi1); + OSMO_ASSERT(ctx->p_tmsi != GSM_RESERVED_TMSI); + OSMO_ASSERT(ctx->p_tmsi != ptmsi1); + + received_ptmsi = get_new_ptmsi(&last_dl_parse_ctx); + OSMO_ASSERT(received_ptmsi == ctx->p_tmsi); + ptmsi1 = received_ptmsi; + + /* inject the RA update complete */ + ms_tlli = gprs_tmsi2tlli(ptmsi1, TLLI_LOCAL); + send_0408_message(ctx->gb.llme, ms_tlli, &raid1, + ra_upd_complete, ARRAY_SIZE(ra_upd_complete)); + + /* we don't expect a response */ + OSMO_ASSERT(sgsn_tx_counter == 0); + + OSMO_ASSERT(ctx->gmm_state == GMM_REGISTERED_NORMAL); + OSMO_ASSERT(ctx->p_tmsi_old == 0); + OSMO_ASSERT(ctx->p_tmsi == ptmsi1); + OSMO_ASSERT(ctx->gb.tlli == ms_tlli); + + printf(" - RA Update Request (RA 1 -> RA 2)\n"); + + /* inject the RA update request */ + ms_tlli = gprs_tmsi2tlli(ptmsi1, TLLI_FOREIGN); + + /* It is coming from RA 1 => ra_upd_req1 */ + send_0408_message(ctx->gb.llme, ms_tlli, &raid2, + ra_upd_req1, ARRAY_SIZE(ra_upd_req1)); + + /* we expect an RA update accept */ + OSMO_ASSERT(sgsn_tx_counter == 1); + OSMO_ASSERT(last_dl_parse_ctx.g48_hdr->msg_type == GSM48_MT_GMM_RA_UPD_ACK); + + printf(" - RA Update Request (RA other -> RA 2)\n"); + + /* inject the RA update request */ + ms_tlli = gprs_tmsi2tlli(0x12345678, TLLI_FOREIGN); + + /* It is coming from RA 1 => ra_upd_req1 */ + send_0408_message(ctx->gb.llme, ms_tlli, &raid2, + ra_upd_req_other, ARRAY_SIZE(ra_upd_req_other)); + + /* we expect an RA update reject (and a LLC XID RESET) */ + OSMO_ASSERT(sgsn_tx_counter == 2); + OSMO_ASSERT(last_dl_parse_ctx.g48_hdr->msg_type == GSM48_MT_GMM_RA_UPD_REJ); + /* this has killed the LLE/LLME */ + + printf(" - Attach Request (RA 2)\n"); + + /* Create a LLE/LLME */ + OSMO_ASSERT(count(gprs_llme_list()) == 1); + lle = gprs_lle_get_or_create(ms_tlli, 3); + OSMO_ASSERT(count(gprs_llme_list()) == 1); + + /* inject the attach request */ + send_0408_message(lle->llme, ms_tlli, &raid2, + attach_req2, ARRAY_SIZE(attach_req2)); + + ctx = sgsn_mm_ctx_by_tlli(ms_tlli, &raid2); + OSMO_ASSERT(ctx != NULL); + OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); + OSMO_ASSERT(ctx->p_tmsi != GSM_RESERVED_TMSI); + + /* we expect an attach accept */ + OSMO_ASSERT(sgsn_tx_counter == 1); + OSMO_ASSERT(last_dl_parse_ctx.g48_hdr->msg_type == GSM48_MT_GMM_ATTACH_ACK); + + received_ptmsi = get_new_ptmsi(&last_dl_parse_ctx); + OSMO_ASSERT(received_ptmsi == ctx->p_tmsi); + ptmsi1 = received_ptmsi; + + /* inject the attach complete */ + ms_tlli = gprs_tmsi2tlli(ptmsi1, TLLI_LOCAL); + ictx = sgsn_mm_ctx_by_tlli(ms_tlli, &raid2); + OSMO_ASSERT(ictx != NULL); + OSMO_ASSERT(ictx == ctx); + + send_0408_message(ctx->gb.llme, ms_tlli, &raid2, + attach_compl, ARRAY_SIZE(attach_compl)); + + /* we don't expect a response */ + OSMO_ASSERT(sgsn_tx_counter == 0); + + OSMO_ASSERT(ctx->gmm_state == GMM_REGISTERED_NORMAL); + OSMO_ASSERT(ctx->p_tmsi_old == 0); + OSMO_ASSERT(ctx->p_tmsi == ptmsi1); + + printf(" - RA Update Request (RA 2 -> RA 2)\n"); + + /* inject the RA update request */ + send_0408_message(ctx->gb.llme, ms_tlli, &raid2, + ra_upd_req2, ARRAY_SIZE(ra_upd_req2)); + + /* we expect an RA update accept */ + OSMO_ASSERT(sgsn_tx_counter == 1); + OSMO_ASSERT(last_dl_parse_ctx.g48_hdr->msg_type == GSM48_MT_GMM_RA_UPD_ACK); + + OSMO_ASSERT(ctx->gmm_state == GMM_COMMON_PROC_INIT); + OSMO_ASSERT(ctx->p_tmsi_old == ptmsi1); + OSMO_ASSERT(ctx->p_tmsi != GSM_RESERVED_TMSI); + OSMO_ASSERT(ctx->p_tmsi != ptmsi1); + + received_ptmsi = get_new_ptmsi(&last_dl_parse_ctx); + OSMO_ASSERT(received_ptmsi == ctx->p_tmsi); + ptmsi1 = received_ptmsi; + + /* inject the RA update complete */ + ms_tlli = gprs_tmsi2tlli(ptmsi1, TLLI_LOCAL); + send_0408_message(ctx->gb.llme, ms_tlli, &raid2, + ra_upd_complete, ARRAY_SIZE(ra_upd_complete)); + + /* we don't expect a response */ + OSMO_ASSERT(sgsn_tx_counter == 0); + + OSMO_ASSERT(ctx->gmm_state == GMM_REGISTERED_NORMAL); + OSMO_ASSERT(ctx->p_tmsi_old == 0); + OSMO_ASSERT(ctx->p_tmsi == ptmsi1); + OSMO_ASSERT(ctx->gb.tlli == ms_tlli); + + + /* inject the detach */ + send_0408_message(ctx->gb.llme, ms_tlli, &raid2, + detach_req, ARRAY_SIZE(detach_req)); + + /* verify that things are gone */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + ictx = sgsn_mm_ctx_by_tlli(ms_tlli, &raid2); + OSMO_ASSERT(!ictx); + + sgsn->cfg.auth_policy = saved_auth_policy; + + cleanup_test(); +} + +static void test_apn_matching(void) +{ + struct apn_ctx *actx, *actxs[9]; + + printf("Testing APN matching\n"); + + actxs[0] = sgsn_apn_ctx_find_alloc("*.test", ""); + actxs[1] = sgsn_apn_ctx_find_alloc("*.def.test", ""); + actxs[2] = sgsn_apn_ctx_find_alloc("abc.def.test", ""); + actxs[3] = NULL; + + actxs[4] = sgsn_apn_ctx_find_alloc("abc.def.test", "456"); + actxs[5] = sgsn_apn_ctx_find_alloc("abc.def.test", "456123"); + actxs[6] = sgsn_apn_ctx_find_alloc("*.def.test", "456"); + actxs[7] = sgsn_apn_ctx_find_alloc("*.def.test", "456123"); + + actxs[8] = sgsn_apn_ctx_find_alloc("ghi.def.test", "456"); + + actx = sgsn_apn_ctx_match("abc.def.test", "12345678"); + OSMO_ASSERT(actx == actxs[2]); + actx = sgsn_apn_ctx_match("aBc.dEf.test", "12345678"); + OSMO_ASSERT(actx == actxs[2]); + actx = sgsn_apn_ctx_match("xyz.def.test", "12345678"); + OSMO_ASSERT(actx == actxs[1]); + actx = sgsn_apn_ctx_match("xyz.dEf.test", "12345678"); + OSMO_ASSERT(actx == actxs[1]); + actx = sgsn_apn_ctx_match("xyz.uvw.test", "12345678"); + OSMO_ASSERT(actx == actxs[0]); + actx = sgsn_apn_ctx_match("xyz.uvw.foo", "12345678"); + OSMO_ASSERT(actx == NULL); + + actxs[3] = sgsn_apn_ctx_find_alloc("*", ""); + actx = sgsn_apn_ctx_match("xyz.uvw.foo", "12345678"); + OSMO_ASSERT(actx == actxs[3]); + + actx = sgsn_apn_ctx_match("abc.def.test", "45699900"); + OSMO_ASSERT(actx == actxs[4]); + + actx = sgsn_apn_ctx_match("xyz.def.test", "45699900"); + OSMO_ASSERT(actx == actxs[6]); + + actx = sgsn_apn_ctx_match("abc.def.test", "45612300"); + OSMO_ASSERT(actx == actxs[5]); + + actx = sgsn_apn_ctx_match("xyz.def.test", "45612300"); + OSMO_ASSERT(actx == actxs[7]); + + actx = sgsn_apn_ctx_match("ghi.def.test", "45699900"); + OSMO_ASSERT(actx == actxs[8]); + + actx = sgsn_apn_ctx_match("ghi.def.test", "45612300"); + OSMO_ASSERT(actx == actxs[7]); + + /* Free APN contexts and check how the matching changes */ + + sgsn_apn_ctx_free(actxs[7]); + actx = sgsn_apn_ctx_match("ghi.def.test", "45612300"); + OSMO_ASSERT(actx == actxs[8]); + + sgsn_apn_ctx_free(actxs[8]); + actx = sgsn_apn_ctx_match("ghi.def.test", "45612300"); + OSMO_ASSERT(actx == actxs[6]); + + sgsn_apn_ctx_free(actxs[6]); + actx = sgsn_apn_ctx_match("ghi.def.test", "45612300"); + OSMO_ASSERT(actx == actxs[1]); + + sgsn_apn_ctx_free(actxs[5]); + actx = sgsn_apn_ctx_match("abc.def.test", "45612300"); + OSMO_ASSERT(actx == actxs[4]); + + sgsn_apn_ctx_free(actxs[4]); + actx = sgsn_apn_ctx_match("abc.def.test", "45612300"); + OSMO_ASSERT(actx == actxs[2]); + + sgsn_apn_ctx_free(actxs[2]); + actx = sgsn_apn_ctx_match("abc.def.test", "12345678"); + OSMO_ASSERT(actx == actxs[1]); + + sgsn_apn_ctx_free(actxs[1]); + actx = sgsn_apn_ctx_match("abc.def.test", "12345678"); + OSMO_ASSERT(actx == actxs[0]); + + sgsn_apn_ctx_free(actxs[0]); + actx = sgsn_apn_ctx_match("abc.def.test", "12345678"); + OSMO_ASSERT(actx == actxs[3]); + + sgsn_apn_ctx_free(actxs[3]); + actx = sgsn_apn_ctx_match("abc.def.test", "12345678"); + OSMO_ASSERT(actx == NULL); + + cleanup_test(); +} + +struct sgsn_subscriber_pdp_data* sgsn_subscriber_pdp_data_alloc( + struct sgsn_subscriber_data *sdata); + +static void test_ggsn_selection(void) +{ + struct apn_ctx *actxs[4]; + struct sgsn_ggsn_ctx *ggc, *ggcs[3]; + struct gprs_subscr *s1; + const char *imsi1 = "1234567890"; + struct sgsn_mm_ctx *ctx; + struct gprs_ra_id raid = { 0, }; + uint32_t local_tlli = 0xffeeddcc; + enum gsm48_gsm_cause gsm_cause; + struct tlv_parsed tp; + uint8_t apn_enc[GSM_APN_LENGTH + 10]; + struct sgsn_subscriber_pdp_data *pdp_data; + char apn_str[GSM_APN_LENGTH]; + + printf("Testing GGSN selection\n"); + + gsup_client_send_cb = my_gsup_client_send_dummy; + + /* Check for emptiness */ + OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL); + + /* Create a context */ + OSMO_ASSERT(count(gprs_llme_list()) == 0); + ctx = alloc_mm_ctx(local_tlli, &raid); + osmo_strlcpy(ctx->imsi, imsi1, sizeof(ctx->imsi)); + + /* Allocate and attach a subscriber */ + s1 = gprs_subscr_get_or_create_by_mmctx(ctx); + assert_subscr(s1, imsi1); + + tp.lv[GSM48_IE_GSM_APN].len = 0; + tp.lv[GSM48_IE_GSM_APN].val = apn_enc; + + /* TODO: Add PDP info entries to s1 */ + + ggcs[0] = sgsn_ggsn_ctx_find_alloc(0); + ggcs[1] = sgsn_ggsn_ctx_find_alloc(1); + ggcs[2] = sgsn_ggsn_ctx_find_alloc(2); + + actxs[0] = sgsn_apn_ctx_find_alloc("test.apn", "123456"); + actxs[0]->ggsn = ggcs[0]; + actxs[1] = sgsn_apn_ctx_find_alloc("*.apn", "123456"); + actxs[1]->ggsn = ggcs[1]; + actxs[2] = sgsn_apn_ctx_find_alloc("*", "456789"); + actxs[2]->ggsn = ggcs[2]; + + pdp_data = sgsn_subscriber_pdp_data_alloc(s1->sgsn_data); + pdp_data->context_id = 1; + pdp_data->pdp_type = 0x0121; + osmo_strlcpy(pdp_data->apn_str, "*", sizeof(pdp_data->apn_str)); + + /* Resolve GGSNs */ + + tp.lv[GSM48_IE_GSM_APN].len = + gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Test.Apn"); + + ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); + OSMO_ASSERT(ggc != NULL); + OSMO_ASSERT(ggc->id == 0); + OSMO_ASSERT(strcmp(apn_str, "Test.Apn") == 0); + + tp.lv[GSM48_IE_GSM_APN].len = + gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Other.Apn"); + + ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); + OSMO_ASSERT(ggc != NULL); + OSMO_ASSERT(ggc->id == 1); + OSMO_ASSERT(strcmp(apn_str, "Other.Apn") == 0); + + tp.lv[GSM48_IE_GSM_APN].len = 0; + tp.lv[GSM48_IE_GSM_APN].val = NULL; + + ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); + OSMO_ASSERT(ggc != NULL); + OSMO_ASSERT(ggc->id == 0); + OSMO_ASSERT(strcmp(apn_str, "") == 0); + + actxs[3] = sgsn_apn_ctx_find_alloc("*", "123456"); + actxs[3]->ggsn = ggcs[2]; + ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); + OSMO_ASSERT(ggc != NULL); + OSMO_ASSERT(ggc->id == 2); + OSMO_ASSERT(strcmp(apn_str, "") == 0); + + sgsn_apn_ctx_free(actxs[3]); + tp.lv[GSM48_IE_GSM_APN].val = apn_enc; + + tp.lv[GSM48_IE_GSM_APN].len = + gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Foo.Bar"); + + ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); + OSMO_ASSERT(ggc == NULL); + OSMO_ASSERT(gsm_cause == GSM_CAUSE_MISSING_APN); + OSMO_ASSERT(strcmp(apn_str, "Foo.Bar") == 0); + + tp.lv[GSM48_IE_GSM_APN].len = sizeof(apn_enc); + ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); + OSMO_ASSERT(ggc == NULL); + OSMO_ASSERT(gsm_cause == GSM_CAUSE_INV_MAND_INFO); + + /* Add PDP data entry to subscriber */ + + osmo_strlcpy(pdp_data->apn_str, "Test.Apn", sizeof(pdp_data->apn_str)); + + tp.lv[GSM48_IE_GSM_APN].len = + gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Test.Apn"); + + ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); + OSMO_ASSERT(ggc != NULL); + OSMO_ASSERT(ggc->id == 0); + OSMO_ASSERT(strcmp(apn_str, "Test.Apn") == 0); + + tp.lv[GSM48_IE_GSM_APN].len = + gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Other.Apn"); + + ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); + OSMO_ASSERT(ggc == NULL); + OSMO_ASSERT(gsm_cause == GSM_CAUSE_REQ_SERV_OPT_NOTSUB); + OSMO_ASSERT(strcmp(apn_str, "") == 0); + + /* Cleanup */ + + gprs_subscr_put(s1); + sgsn_mm_ctx_cleanup_free(ctx); + + assert_no_subscrs(); + + sgsn_apn_ctx_free(actxs[0]); + sgsn_apn_ctx_free(actxs[1]); + sgsn_apn_ctx_free(actxs[2]); + + sgsn_ggsn_ctx_free(ggcs[0]); + sgsn_ggsn_ctx_free(ggcs[1]); + sgsn_ggsn_ctx_free(ggcs[2]); + + gsup_client_send_cb = __real_gsup_client_send; + + cleanup_test(); +} + +static struct log_info_cat gprs_categories[] = { + [DMM] = { + .name = "DMM", + .description = "Layer3 Mobility Management (MM)", + .color = "\033[1;33m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DPAG] = { + .name = "DPAG", + .description = "Paging Subsystem", + .color = "\033[1;38m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DMEAS] = { + .name = "DMEAS", + .description = "Radio Measurement Processing", + .enabled = 0, .loglevel = LOGL_NOTICE, + }, + [DREF] = { + .name = "DREF", + .description = "Reference Counting", + .enabled = 0, .loglevel = LOGL_NOTICE, + }, + [DGPRS] = { + .name = "DGPRS", + .description = "GPRS Packet Service", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DNS] = { + .name = "DNS", + .description = "GPRS Network Service (NS)", + .enabled = 1, .loglevel = LOGL_INFO, + }, + [DBSSGP] = { + .name = "DBSSGP", + .description = "GPRS BSS Gateway Protocol (BSSGP)", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DLLC] = { + .name = "DLLC", + .description = "GPRS Logical Link Control Protocol (LLC)", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DSNDCP] = { + .name = "DSNDCP", + .description = "GPRS Sub-Network Dependent Control Protocol (SNDCP)", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, +}; + +static struct log_info info = { + .cat = gprs_categories, + .num_cat = ARRAY_SIZE(gprs_categories), +}; + +int main(int argc, char **argv) +{ + void *osmo_sgsn_ctx; + void *msgb_ctx; + + osmo_init_logging(&info); + osmo_sgsn_ctx = talloc_named_const(NULL, 0, "osmo_sgsn"); + tall_bsc_ctx = talloc_named_const(osmo_sgsn_ctx, 0, "bsc"); + msgb_ctx = msgb_talloc_ctx_init(osmo_sgsn_ctx, 0); + + sgsn_rate_ctr_init(); + sgsn_auth_init(); + gprs_subscr_init(sgsn); + + test_llme(); + test_subscriber(); + test_auth_triplets(); + test_subscriber_gsup(); + test_gmm_detach(); + test_gmm_detach_power_off(); + test_gmm_detach_no_mmctx(); + test_gmm_detach_accept_unexpected(); + test_gmm_status_no_mmctx(); + test_gmm_attach_acl(); + test_gmm_attach_subscr(); + test_gmm_attach_subscr_fake_auth(); + test_gmm_attach_subscr_real_auth(); + test_gmm_attach_subscr_gsup_auth(0); + test_gmm_attach_subscr_gsup_auth(1); + test_gmm_attach_subscr_real_gsup_auth(0); + test_gmm_reject(); + test_gmm_cancel(); + test_gmm_ptmsi_allocation(); + test_gmm_routing_areas(); + test_apn_matching(); + test_ggsn_selection(); + printf("Done\n"); + + talloc_report_full(osmo_sgsn_ctx, stderr); + OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1); + OSMO_ASSERT(talloc_total_blocks(tall_bsc_ctx) == 2); + return 0; +} + + +/* stubs */ +struct osmo_prim_hdr; +int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) +{ + abort(); +} |