lynxis lazus submitted this change.
Re-introduce Iu/UTRAN support
Add support for UTRAN routing areas.
Change-Id: I1b1aedd2a7c358bd388aa3d8a9f3c6a0011b4889
---
M include/osmocom/sgsn/gprs_routing_area.h
M src/sgsn/gprs_ranap.c
M src/sgsn/gprs_routing_area.c
M tests/gprs_routing_area/gprs_routing_area_test.c
4 files changed, 206 insertions(+), 2 deletions(-)
diff --git a/include/osmocom/sgsn/gprs_routing_area.h b/include/osmocom/sgsn/gprs_routing_area.h
index a807227..60dfd71 100644
--- a/include/osmocom/sgsn/gprs_routing_area.h
+++ b/include/osmocom/sgsn/gprs_routing_area.h
@@ -56,6 +56,12 @@
* For UTRAN only do a LAC/RAC <> RNC relation and don't have a specific cell relation.
*/
enum sgsn_ra_ran_type ran_type;
+ union {
+ struct {
+ /* the RNC id must be the same for a given Routing Area */
+ struct osmo_rnc_id rnc_id;
+ } utran;
+ } u;
/* GERAN/UTRAN: cells contains a list of sgsn_ra_cells which are alive */
struct llist_head cells_alive_list;
@@ -77,8 +83,7 @@
} geran;
struct {
- /* TODO: unused */
- uint16_t rncid;
+ /* the RNC id must be the same for a given Routing Area */
uint16_t sac;
} utran;
} u;
@@ -107,10 +112,15 @@
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_cgi(const struct osmo_cell_global_id *cgi);
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_ra(const struct sgsn_ra *ra, uint16_t cell_id);
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_gb(uint16_t nsei, uint16_t bvci);
+
+/* UTRAN */
+int sgsn_ra_utran_register(const struct osmo_routing_area_id *rai, const struct osmo_rnc_id *rnc_id);
+
struct sgsn_ra *sgsn_ra_geran_get_ra(const struct osmo_routing_area_id *rai);
/* Page the whole routing area for this mmctx */
int sgsn_ra_geran_page_ra(const struct osmo_routing_area_id *rai, struct sgsn_mm_ctx *mmctx);
+struct sgsn_ra *sgsn_ra_utran_get_ra(const struct osmo_routing_area_id *rai);
/*
* return value for callbacks.
@@ -125,3 +135,6 @@
typedef int (sgsn_ra_cb_t)(struct sgsn_ra_cell *ra_cell, void *cb_data);
int sgsn_ra_foreach_cell(struct sgsn_ra *ra, sgsn_ra_cb_t *cb, void *cb_data);
int sgsn_ra_foreach_cell2(struct osmo_routing_area_id *rai, sgsn_ra_cb_t *cb, void *cb_data);
+
+/* Page the whole routing area for this mmctx */
+int sgsn_ra_utran_page_ra(const struct osmo_routing_area_id *rai, const struct sgsn_mm_ctx *mmctx);
diff --git a/src/sgsn/gprs_ranap.c b/src/sgsn/gprs_ranap.c
index 3e2c891..08fc625 100644
--- a/src/sgsn/gprs_ranap.c
+++ b/src/sgsn/gprs_ranap.c
@@ -38,6 +38,7 @@
#include <osmocom/sgsn/gprs_ranap.h>
#include <osmocom/sgsn/gprs_gmm_attach.h>
#include <osmocom/sgsn/gprs_mm_state_iu_fsm.h>
+#include <osmocom/sgsn/gprs_routing_area.h>
#include <osmocom/sgsn/gtp_ggsn.h>
#include <osmocom/sgsn/gtp.h>
#include <osmocom/sgsn/pdpctx.h>
@@ -202,12 +203,23 @@
int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data)
{
+ struct ranap_iu_event_new_area *new_area;
+
switch (type) {
case RANAP_IU_EVENT_RAB_ASSIGN:
case RANAP_IU_EVENT_IU_RELEASE:
case RANAP_IU_EVENT_LINK_INVALIDATED:
case RANAP_IU_EVENT_SECURITY_MODE_COMPLETE:
return sgsn_ranap_iu_event_mmctx(ctx, type, data);
+ case RANAP_IU_EVENT_NEW_AREA:
+ /* inform the Routing Area code about a new RA for Iu */
+ new_area = data;
+
+ /* Only interesting in Routing Area changes, but not Location Area */
+ if (new_area->cell_type != RANAP_IU_NEW_RAC)
+ return 0;
+
+ return sgsn_ra_utran_register(new_area->u.rai, new_area->rnc_id);
default:
LOGP(DRANAP, LOGL_NOTICE, "Iu: Unknown event received: type: %d\n", type);
return -1;
diff --git a/src/sgsn/gprs_routing_area.c b/src/sgsn/gprs_routing_area.c
index 721f0c9..4400d6d 100644
--- a/src/sgsn/gprs_routing_area.c
+++ b/src/sgsn/gprs_routing_area.c
@@ -24,6 +24,7 @@
#include <osmocom/core/logging.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/utils.h>
+#include <osmocom/gsm/gsm23003.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/sgsn/debug.h>
#include <osmocom/sgsn/gprs_bssgp.h>
@@ -181,6 +182,19 @@
return NULL;
}
+struct sgsn_ra *sgsn_ra_utran_get_ra(const struct osmo_routing_area_id *ra_id)
+{
+ struct sgsn_ra *ra = sgsn_ra_get_ra(ra_id);
+
+ if (!ra)
+ return ra;
+
+ if (ra->ran_type == RA_TYPE_UTRAN_Iu)
+ return ra;
+
+ return NULL;
+}
+
int sgsn_ra_foreach_cell(struct sgsn_ra *ra, sgsn_ra_cb_t *cb, void *cb_data)
{
struct sgsn_ra_cell *cell, *tmp;
@@ -419,6 +433,74 @@
return ret;
}
+#ifdef BUILD_IU
+/* Register a new UTRAN Routing Area if possible.
+ * Return 0 on success and < 0 on failure. */
+int sgsn_ra_utran_register(const struct osmo_routing_area_id *rai, const struct osmo_rnc_id *rnc_id)
+{
+ struct sgsn_ra *ra = sgsn_ra_get_ra(rai);
+ if (!ra) {
+ ra = sgsn_ra_alloc(rai, RA_TYPE_UTRAN_Iu);
+ if (!ra) {
+ LOGP(DRA, LOGL_ERROR, "Couldn't create new RA for %s ran type %s\n",
+ osmo_rai_name2(rai), get_value_string(sgsn_ra_ran_type_names, ra->ran_type));
+ return -ENOMEM;
+ }
+ ra->u.utran.rnc_id = *rnc_id;
+ LOGRA(LOGL_INFO, ra, "New UTRAN RA by RNC %s\n", osmo_rnc_id_name(&ra->u.utran.rnc_id));
+ return 0;
+ }
+
+ if (ra->ran_type == RA_TYPE_GERAN_Gb) {
+ LOGRA(LOGL_ERROR, ra, "rejecting new RA of type %s, because already present RA has ran type %s\n",
+ get_value_string(sgsn_ra_ran_type_names, RA_TYPE_UTRAN_Iu),
+ get_value_string(sgsn_ra_ran_type_names, ra->ran_type));
+ return -ENOENT;
+ }
+
+ /* RA already known */
+ if (osmo_rnc_id_cmp(&ra->u.utran.rnc_id, rnc_id) == 0)
+ return 0;
+
+ /* RNC id changed */
+ char new_rnc_id_name[32];
+ osmo_rnc_id_name_buf(new_rnc_id_name, sizeof(new_rnc_id_name), rnc_id);
+ LOGRA(LOGL_INFO, ra, "RNC Id changed from %s to %s\n",
+ osmo_rnc_id_name(&ra->u.utran.rnc_id), new_rnc_id_name);
+
+ ra->u.utran.rnc_id = *rnc_id;
+ return 0;
+}
+
+int sgsn_ra_utran_page_ra(const struct osmo_routing_area_id *ra_id, const struct sgsn_mm_ctx *mmctx)
+{
+ struct sgsn_ra *ra;
+ rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PAGING_PS));
+
+ ra = sgsn_ra_utran_get_ra(ra_id);
+ if (!ra)
+ return -ENOENT;
+
+ /* Try to page by TMSI if possible */
+ if (mmctx->p_tmsi != GSM_RESERVED_TMSI)
+ return ranap_iu_page_ps2(mmctx->imsi, &mmctx->p_tmsi, ra_id);
+ if (mmctx->p_tmsi_old != GSM_RESERVED_TMSI)
+ return ranap_iu_page_ps2(mmctx->imsi, &mmctx->p_tmsi_old, ra_id);
+
+ /* Page by IMSI */
+ return ranap_iu_page_ps2(mmctx->imsi, NULL, ra_id);
+}
+#else
+int sgsn_ra_utran_page_ra(const struct osmo_routing_area_id *ra_id, const struct sgsn_mm_ctx *mmctx)
+{
+ return -1;
+}
+int sgsn_ra_utran_register(const struct osmo_routing_area_id *rai, const struct osmo_rnc_id *rnc_id)
+{
+ return -1;
+}
+#endif /* BUILD_IU */
+
void sgsn_ra_init(struct sgsn_instance *inst)
{
inst->routing_area = talloc_zero(inst, struct sgsn_ra_global);
diff --git a/tests/gprs_routing_area/gprs_routing_area_test.c b/tests/gprs_routing_area/gprs_routing_area_test.c
index b9840b9..b4b5b21 100644
--- a/tests/gprs_routing_area/gprs_routing_area_test.c
+++ b/tests/gprs_routing_area/gprs_routing_area_test.c
@@ -24,6 +24,7 @@
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/apn.h>
+#include <osmocom/gsm/gsm23003.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/gsup.h>
#include <osmocom/gprs/gprs_bssgp.h>
@@ -537,6 +538,98 @@
cleanup_test();
}
+/* check if UTRAN RA gets rejected, if a GERAN RA/cell with the same LAC is already registered
+ * The SGSN does not support the same LAC/RA for GERAN and UTRAN at the same time.
+ */
+void test_routing_area_mv_utran_geran_reject(void)
+{
+ int rc;
+
+ /* GERAN */
+ struct osmo_routing_area_id geran_rai = {
+ .lac = {
+ .plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
+ .lac = 24
+ },
+ .rac = 43
+ };
+ struct osmo_cell_global_id_ps cgi_ps = {
+ .rai = geran_rai,
+ .cell_identity = 9998,
+ };
+ uint16_t nsei = 2, bvci = 3;
+
+ /* UTRAN */
+ struct osmo_routing_area_id utran_rai = {
+ .lac = {
+ .plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
+ .lac = 24
+ },
+ .rac = 43
+ };
+ struct osmo_rnc_id rnc_id = {
+ .plmn = utran_rai.lac.plmn,
+ .rnc_id = 2222
+ };
+
+ sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
+
+ /* Registering UTRAN RA */
+ rc = sgsn_ra_utran_register(&utran_rai, &rnc_id);
+ OSMO_ASSERT(rc == 0);
+
+ /* Registering GERAN RA/cell via BVC Reset Ind (should fail) */
+ rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci, &cgi_ps);
+ OSMO_ASSERT(rc != 0);
+
+ cleanup_test();
+}
+
+/* check if a UTRAN RA with the same LAC as an already register GERAN RA gets rejected */
+void test_routing_area_mv_geran_utran_reject(void)
+{
+ int rc;
+
+ /* GERAN */
+ struct osmo_routing_area_id geran_rai = {
+ .lac = {
+ .plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
+ .lac = 24
+ },
+ .rac = 43
+ };
+ struct osmo_cell_global_id_ps cgi_ps = {
+ .rai = geran_rai,
+ .cell_identity = 9998,
+ };
+ uint16_t nsei = 2, bvci = 3;
+
+ /* UTRAN */
+ struct osmo_routing_area_id utran_rai = {
+ .lac = {
+ .plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
+ .lac = 24
+ },
+ .rac = 43
+ };
+ struct osmo_rnc_id rnc_id = {
+ .plmn = utran_rai.lac.plmn,
+ .rnc_id = 2222
+ };
+
+ sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
+
+ /* Registering GERAN RA/cell via BVC Reset Ind */
+ rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci, &cgi_ps);
+ OSMO_ASSERT(rc == 0);
+
+ /* Registering UTRAN RA (should fail) */
+ rc = sgsn_ra_utran_register(&utran_rai, &rnc_id);
+ OSMO_ASSERT(rc != 0);
+
+ cleanup_test();
+}
+
static struct log_info_cat gprs_categories[] = {
[DMM] = {
.name = "DMM",
@@ -601,6 +694,10 @@
test_routing_area_paging();
test_routing_area_geran_geran_sig_reset();
test_routing_area_geran_geran_bvci_change();
+#ifdef BUILD_IU
+ test_routing_area_mv_geran_utran_reject();
+ test_routing_area_mv_utran_geran_reject();
+#endif
printf("Done\n");
talloc_report_full(osmo_sgsn_ctx, stderr);
To view, visit change 40813. To unsubscribe, or for help writing mail filters, visit settings.