Re-introduce Iu/UTRAN support

Add support for UTRAN routing areas.

Change-Id: I1b1aedd2a7c358bd388aa3d8a9f3c6a0011b4889
This commit is contained in:
Alexander Couzens
2025-07-15 18:48:30 +02:00
parent b1444a6c11
commit 02fbdb59c2
4 changed files with 206 additions and 2 deletions

View File

@@ -56,6 +56,12 @@ struct sgsn_ra {
* 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 @@ struct sgsn_ra_cell {
} 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_lai(const struct osmo_location_ar
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 @@ int sgsn_ra_geran_page_ra(const struct osmo_routing_area_id *rai, struct sgsn_mm
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);

View File

@@ -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 @@ static int sgsn_ranap_iu_event_mmctx(struct ranap_ue_conn_ctx *ctx, enum ranap_i
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;

View File

@@ -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 @@ struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_gb(uint16_t nsei, uint16_t bvci)
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 @@ int sgsn_ra_geran_page_ra(const struct osmo_routing_area_id *rai, struct sgsn_mm
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);

View File

@@ -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 @@ void test_routing_area_geran_geran_bvci_change(void)
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 @@ int main(int argc, char **argv)
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);