iu client: store multiple LAC,RAC per RNC = fix paging for multiple RNC
Introduce a list of LAC+RAC entries for each RNC, hence allow serving more than one LAC per OsmoHNBGW. iu_client is used by OsmoMSC and OsmoSGSN, both will be able to page successfully in a setup with multiple LACs (read: multiple hNodeB) connected to an OsmoHNBGW. Ensure that each LAC,RAC is registered with at most one RNC Id. If a LAC,RAC shows up on a different RNC Id than before, move it over to the new RNC Id. Future patches should probably add: * timeouts of RNC Id / LAC,RAC validity, to remove unused entries. * VTY/CTRL commands to introspect which RNCs and LAC,RACs are listed. * VTY/CTRL commands to remove RNC Id / LAC,RAC entries. Change-Id: I189f8e2663353276b1c615d2f78455dafe568045
This commit is contained in:
parent
accb78000b
commit
69888c86ed
209
src/iu_client.c
209
src/iu_client.c
|
@ -47,6 +47,15 @@ struct iu_grnc_id {
|
||||||
uint16_t rnc_id;
|
uint16_t rnc_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct iu_lac_rac_entry {
|
||||||
|
struct llist_head entry;
|
||||||
|
|
||||||
|
/* LAC: Location Area Code (for CS and PS) */
|
||||||
|
uint16_t lac;
|
||||||
|
/* RAC: Routing Area Code (for PS only) */
|
||||||
|
uint8_t rac;
|
||||||
|
};
|
||||||
|
|
||||||
/* A remote RNC (Radio Network Controller, like BSC but for UMTS) that has
|
/* A remote RNC (Radio Network Controller, like BSC but for UMTS) that has
|
||||||
* called us and is currently reachable at the given osmo_sccp_addr. So, when we
|
* called us and is currently reachable at the given osmo_sccp_addr. So, when we
|
||||||
* know a LAC for a subscriber, we can page it at the RNC matching that LAC or
|
* know a LAC for a subscriber, we can page it at the RNC matching that LAC or
|
||||||
|
@ -58,9 +67,10 @@ struct ranap_iu_rnc {
|
||||||
struct llist_head entry;
|
struct llist_head entry;
|
||||||
|
|
||||||
uint16_t rnc_id;
|
uint16_t rnc_id;
|
||||||
uint16_t lac; /* Location Area Code (used for CS and PS) */
|
|
||||||
uint8_t rac; /* Routing Area Code (used for PS only) */
|
|
||||||
struct osmo_sccp_addr sccp_addr;
|
struct osmo_sccp_addr sccp_addr;
|
||||||
|
|
||||||
|
/* A list of struct iu_lac_rac_entry */
|
||||||
|
struct llist_head lac_rac_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
void *talloc_iu_ctx;
|
void *talloc_iu_ctx;
|
||||||
|
@ -82,6 +92,9 @@ int iu_log_subsystem = 0;
|
||||||
#define LOGPIU(level, fmt, args...) \
|
#define LOGPIU(level, fmt, args...) \
|
||||||
LOGP(iu_log_subsystem, level, fmt, ## args)
|
LOGP(iu_log_subsystem, level, fmt, ## args)
|
||||||
|
|
||||||
|
#define LOGPIUC(level, fmt, args...) \
|
||||||
|
LOGPC(iu_log_subsystem, level, fmt, ## args)
|
||||||
|
|
||||||
static LLIST_HEAD(ue_conn_ctx_list);
|
static LLIST_HEAD(ue_conn_ctx_list);
|
||||||
static LLIST_HEAD(rnc_list);
|
static LLIST_HEAD(rnc_list);
|
||||||
|
|
||||||
|
@ -119,53 +132,109 @@ static struct ranap_ue_conn_ctx *ue_conn_ctx_find(uint32_t conn_id)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct ranap_iu_rnc *iu_rnc_alloc(uint16_t rnc_id, uint16_t lac, uint8_t rac,
|
static struct ranap_iu_rnc *iu_rnc_alloc(uint16_t rnc_id, struct osmo_sccp_addr *addr)
|
||||||
struct osmo_sccp_addr *addr)
|
|
||||||
{
|
{
|
||||||
struct ranap_iu_rnc *rnc = talloc_zero(talloc_iu_ctx, struct ranap_iu_rnc);
|
struct ranap_iu_rnc *rnc = talloc_zero(talloc_iu_ctx, struct ranap_iu_rnc);
|
||||||
|
OSMO_ASSERT(rnc);
|
||||||
|
|
||||||
|
INIT_LLIST_HEAD(&rnc->lac_rac_list);
|
||||||
|
|
||||||
rnc->rnc_id = rnc_id;
|
rnc->rnc_id = rnc_id;
|
||||||
rnc->lac = lac;
|
|
||||||
rnc->rac = rac;
|
|
||||||
rnc->sccp_addr = *addr;
|
rnc->sccp_addr = *addr;
|
||||||
llist_add(&rnc->entry, &rnc_list);
|
llist_add(&rnc->entry, &rnc_list);
|
||||||
|
|
||||||
LOGPIU(LOGL_NOTICE, "New RNC %d (LAC=%d RAC=%d)\n",
|
LOGPIU(LOGL_NOTICE, "New RNC %d at %s\n", rnc->rnc_id, osmo_sccp_addr_dump(addr));
|
||||||
rnc->rnc_id, rnc->lac, rnc->rac);
|
|
||||||
|
|
||||||
return rnc;
|
return rnc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Find a match for the given LAC (and RAC). For CS, pass rac as 0.
|
||||||
|
* If rnc and lre pointers are not NULL, *rnc / *lre are set to NULL if no match is found, or to the
|
||||||
|
* match if a match is found. Return true if a match is found. */
|
||||||
|
static bool iu_rnc_lac_rac_find(struct ranap_iu_rnc **rnc, struct iu_lac_rac_entry **lre,
|
||||||
|
uint16_t lac, uint8_t rac)
|
||||||
|
{
|
||||||
|
struct ranap_iu_rnc *r;
|
||||||
|
struct iu_lac_rac_entry *e;
|
||||||
|
|
||||||
|
if (rnc)
|
||||||
|
*rnc = NULL;
|
||||||
|
if (lre)
|
||||||
|
*lre = NULL;
|
||||||
|
|
||||||
|
llist_for_each_entry(r, &rnc_list, entry) {
|
||||||
|
llist_for_each_entry(e, &r->lac_rac_list, entry) {
|
||||||
|
if (e->lac == lac && e->rac == rac) {
|
||||||
|
if (rnc)
|
||||||
|
*rnc = r;
|
||||||
|
if (lre)
|
||||||
|
*lre = e;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct ranap_iu_rnc *iu_rnc_id_find(uint16_t rnc_id)
|
||||||
|
{
|
||||||
|
struct ranap_iu_rnc *rnc;
|
||||||
|
llist_for_each_entry(rnc, &rnc_list, entry) {
|
||||||
|
if (rnc->rnc_id == rnc_id)
|
||||||
|
return rnc;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool same_sccp_addr(struct osmo_sccp_addr *a, struct osmo_sccp_addr *b)
|
||||||
|
{
|
||||||
|
char buf[256];
|
||||||
|
osmo_strlcpy(buf, osmo_sccp_addr_dump(a), sizeof(buf));
|
||||||
|
return !strcmp(buf, osmo_sccp_addr_dump(b));
|
||||||
|
}
|
||||||
|
|
||||||
static struct ranap_iu_rnc *iu_rnc_register(uint16_t rnc_id, uint16_t lac,
|
static struct ranap_iu_rnc *iu_rnc_register(uint16_t rnc_id, uint16_t lac,
|
||||||
uint8_t rac, struct osmo_sccp_addr *addr)
|
uint8_t rac, struct osmo_sccp_addr *addr)
|
||||||
{
|
{
|
||||||
struct ranap_iu_rnc *rnc;
|
struct ranap_iu_rnc *rnc;
|
||||||
llist_for_each_entry(rnc, &rnc_list, entry) {
|
struct ranap_iu_rnc *old_rnc;
|
||||||
if (rnc->rnc_id != rnc_id)
|
struct iu_lac_rac_entry *lre;
|
||||||
continue;
|
|
||||||
|
|
||||||
/* We have this RNC Id registered already. Make sure that the
|
/* Make sure we know this rnc_id and that this SCCP address is in our records */
|
||||||
* details match. */
|
rnc = iu_rnc_id_find(rnc_id);
|
||||||
|
|
||||||
/* TODO should a mismatch be an error? */
|
if (rnc) {
|
||||||
if (rnc->lac != lac || rnc->rac != rac)
|
if (!same_sccp_addr(&rnc->sccp_addr, addr)) {
|
||||||
LOGPIU(LOGL_NOTICE, "RNC %d changes its details:"
|
LOGPIU(LOGL_NOTICE, "RNC %u changed its SCCP addr to %s (LAC=%u RAC=%u)\n",
|
||||||
" LAC=%d RAC=%d --> LAC=%d RAC=%d\n",
|
rnc_id, osmo_sccp_addr_dump(addr), lac, rac);
|
||||||
rnc->rnc_id, rnc->lac, rnc->rac,
|
rnc->sccp_addr = *addr;
|
||||||
lac, rac);
|
}
|
||||||
rnc->lac = lac;
|
} else
|
||||||
rnc->rac = rac;
|
rnc = iu_rnc_alloc(rnc_id, addr);
|
||||||
|
|
||||||
if (addr && memcmp(&rnc->sccp_addr, addr, sizeof(*addr)))
|
/* Detect whether the LAC,RAC is already recorded in another RNC */
|
||||||
LOGPIU(LOGL_NOTICE, "RNC %d on New SCCP Addr %s"
|
iu_rnc_lac_rac_find(&old_rnc, &lre, lac, rac);
|
||||||
" (LAC=%d RAC=%d)\n",
|
|
||||||
rnc->rnc_id, osmo_sccp_addr_dump(addr), rnc->lac, rnc->rac);
|
if (old_rnc && old_rnc != rnc) {
|
||||||
rnc->sccp_addr = *addr;
|
/* LAC,RAC already exists in a different RNC */
|
||||||
return rnc;
|
LOGPIU(LOGL_NOTICE, "LAC %u RAC %u moved from RNC %u %s",
|
||||||
|
lac, rac, old_rnc->rnc_id, osmo_sccp_addr_dump(&old_rnc->sccp_addr));
|
||||||
|
LOGPIUC(LOGL_NOTICE, " to RNC %u %s\n",
|
||||||
|
rnc->rnc_id, osmo_sccp_addr_dump(&rnc->sccp_addr));
|
||||||
|
|
||||||
|
llist_del(&lre->entry);
|
||||||
|
llist_add(&lre->entry, &rnc->lac_rac_list);
|
||||||
|
} else if (!old_rnc) {
|
||||||
|
/* LAC,RAC not recorded yet */
|
||||||
|
LOGPIU(LOGL_NOTICE, "RNC %u: new LAC %u RAC %u\n", rnc_id, lac, rac);
|
||||||
|
lre = talloc_zero(rnc, struct iu_lac_rac_entry);
|
||||||
|
lre->lac = lac;
|
||||||
|
lre->rac = rac;
|
||||||
|
llist_add(&lre->entry, &rnc->lac_rac_list);
|
||||||
}
|
}
|
||||||
|
/* else, LAC,RAC already recorded with the current RNC. */
|
||||||
|
|
||||||
/* Not found, make a new one. */
|
return rnc;
|
||||||
return iu_rnc_alloc(rnc_id, lac, rac, addr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
|
@ -602,69 +671,45 @@ static int iu_tx_paging_cmd(struct osmo_sccp_addr *called_addr,
|
||||||
struct msgb *msg;
|
struct msgb *msg;
|
||||||
msg = ranap_new_msg_paging_cmd(imsi, tmsi, is_ps? 1 : 0, paging_cause);
|
msg = ranap_new_msg_paging_cmd(imsi, tmsi, is_ps? 1 : 0, paging_cause);
|
||||||
msg->l2h = msg->data;
|
msg->l2h = msg->data;
|
||||||
osmo_sccp_tx_unitdata_msg(g_scu, &g_local_sccp_addr, called_addr, msg);
|
return osmo_sccp_tx_unitdata_msg(g_scu, &g_local_sccp_addr, called_addr, msg);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int iu_page(const char *imsi, const uint32_t *tmsi_or_ptimsi,
|
static int iu_page(const char *imsi, const uint32_t *tmsi_or_ptmsi,
|
||||||
uint16_t lac, uint8_t rac, bool is_ps)
|
uint16_t lac, uint8_t rac, bool is_ps)
|
||||||
{
|
{
|
||||||
struct ranap_iu_rnc *rnc;
|
struct ranap_iu_rnc *rnc;
|
||||||
int pagings_sent = 0;
|
int rc;
|
||||||
|
const char *log_msg;
|
||||||
|
int log_level;
|
||||||
|
int paged = 0;
|
||||||
|
|
||||||
if (tmsi_or_ptimsi) {
|
iu_rnc_lac_rac_find(&rnc, NULL, lac, rac);
|
||||||
LOGPIU(LOGL_DEBUG, "%s: Looking for RNCs to page for IMSI %s"
|
if (rnc) {
|
||||||
" (paging will use %s %x)\n",
|
if (iu_tx_paging_cmd(&rnc->sccp_addr, imsi, tmsi_or_ptmsi, is_ps, 0) == 0) {
|
||||||
is_ps? "IuPS" : "IuCS",
|
log_msg = "Paging";
|
||||||
imsi,
|
log_level = LOGL_DEBUG;
|
||||||
is_ps? "PTMSI" : "TMSI",
|
paged = 1;
|
||||||
*tmsi_or_ptimsi);
|
} else {
|
||||||
|
log_msg = "Paging failed";
|
||||||
|
log_level = LOGL_ERROR;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
LOGPIU(LOGL_DEBUG, "%s: Looking for RNCs to page for IMSI %s"
|
log_msg = "Found no RNC to Page";
|
||||||
" (paging will use IMSI)\n",
|
log_level = LOGL_ERROR;
|
||||||
is_ps? "IuPS" : "IuCS",
|
|
||||||
imsi
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
llist_for_each_entry(rnc, &rnc_list, entry) {
|
if (is_ps)
|
||||||
if (rnc->lac != lac)
|
LOGPIU(log_level, "IuPS: %s on LAC %d RAC %d", log_msg, lac, rac);
|
||||||
continue;
|
else
|
||||||
if (is_ps && rnc->rac != rac)
|
LOGPIU(log_level, "IuCS: %s on LAC %d", log_msg, lac);
|
||||||
continue;
|
if (rnc)
|
||||||
|
LOGPIUC(log_level, " at SCCP-addr %s", osmo_sccp_addr_dump(&rnc->sccp_addr));
|
||||||
|
if (tmsi_or_ptmsi)
|
||||||
|
LOGPIUC(log_level, ", for %s %08x\n", is_ps? "PTMSI" : "TMSI", *tmsi_or_ptmsi);
|
||||||
|
else
|
||||||
|
LOGPIUC(log_level, ", for IMSI %s\n", imsi);
|
||||||
|
|
||||||
/* Found a match! */
|
return paged;
|
||||||
if (iu_tx_paging_cmd(&rnc->sccp_addr, imsi, tmsi_or_ptimsi, is_ps, 0)
|
|
||||||
== 0) {
|
|
||||||
LOGPIU(LOGL_DEBUG,
|
|
||||||
"%s: Paged for IMSI %s on RNC %d, on SCCP addr %s\n",
|
|
||||||
is_ps? "IuPS" : "IuCS",
|
|
||||||
imsi, rnc->rnc_id, osmo_sccp_addr_dump(&rnc->sccp_addr));
|
|
||||||
pagings_sent ++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Some logging... */
|
|
||||||
if (pagings_sent > 0) {
|
|
||||||
LOGPIU(LOGL_DEBUG,
|
|
||||||
"%s: %d RNCs were paged for IMSI %s.\n",
|
|
||||||
is_ps? "IuPS" : "IuCS",
|
|
||||||
pagings_sent, imsi);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (is_ps) {
|
|
||||||
LOGPIU(LOGL_ERROR, "IuPS: Found no RNC to page for"
|
|
||||||
" LAC %d RAC %d (would have paged IMSI %s)\n",
|
|
||||||
lac, rac, imsi);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LOGPIU(LOGL_ERROR, "IuCS: Found no RNC to page for"
|
|
||||||
" LAC %d (would have paged IMSI %s)\n",
|
|
||||||
lac, imsi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pagings_sent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ranap_iu_page_cs(const char *imsi, const uint32_t *tmsi, uint16_t lac)
|
int ranap_iu_page_cs(const char *imsi, const uint32_t *tmsi, uint16_t lac)
|
||||||
|
|
Loading…
Reference in New Issue