192 lines
5.0 KiB
C
192 lines
5.0 KiB
C
|
/* Manage identity of neighboring BSS cells for inter-MSC handover. */
|
||
|
/*
|
||
|
* (C) 2018-2019 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
|
||
|
* All Rights Reserved
|
||
|
*
|
||
|
* SPDX-License-Identifier: AGPL-3.0+
|
||
|
*
|
||
|
* Author: Neels Hofmeyr
|
||
|
* Author: Stefan Sperling <ssperling@sysmocom.de>
|
||
|
*
|
||
|
* 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 <errno.h>
|
||
|
|
||
|
#include <osmocom/core/linuxlist.h>
|
||
|
#include <osmocom/core/utils.h>
|
||
|
#include <osmocom/gsm/gsm0808.h>
|
||
|
#include <osmocom/sigtran/osmo_ss7.h>
|
||
|
#include <osmocom/sigtran/sccp_helpers.h>
|
||
|
|
||
|
#include <osmocom/msc/neighbor_ident.h>
|
||
|
#include <osmocom/msc/gsm_data.h>
|
||
|
#include <osmocom/msc/sccp_ran.h>
|
||
|
#include <osmocom/msc/cell_id_list.h>
|
||
|
|
||
|
/* XXX greater than or equal to IPA_STRING_MAX (libosmocore) and MAX_PC_STR_LEN (libosmo-sccp). */
|
||
|
#define NEIGHBOR_IDENT_ADDR_STRING_MAX 64
|
||
|
|
||
|
static struct gsm_network *gsmnet;
|
||
|
|
||
|
void neighbor_ident_init(struct gsm_network *net)
|
||
|
{
|
||
|
gsmnet = net;
|
||
|
INIT_LLIST_HEAD(&gsmnet->neighbor_ident_list);
|
||
|
}
|
||
|
|
||
|
int msc_ipa_name_from_str(struct msc_ipa_name *min, const char *name)
|
||
|
{
|
||
|
int rc = osmo_strlcpy(min->buf, name, sizeof(min->buf));
|
||
|
if (rc >= sizeof(min->buf)) {
|
||
|
min->len = 0;
|
||
|
return -1;
|
||
|
}
|
||
|
min->len = rc;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int msc_ipa_name_cmp(const struct msc_ipa_name *a, const struct msc_ipa_name *b)
|
||
|
{
|
||
|
size_t cmp_len;
|
||
|
int rc;
|
||
|
if (a == b)
|
||
|
return 0;
|
||
|
if (!a || !b)
|
||
|
return a ? 1 : -1;
|
||
|
cmp_len = OSMO_MIN(sizeof(a->buf), OSMO_MIN(a->len, b->len));
|
||
|
if (!cmp_len)
|
||
|
rc = 0;
|
||
|
else
|
||
|
rc = memcmp(a->buf, b->buf, cmp_len);
|
||
|
if (rc)
|
||
|
return rc;
|
||
|
if (a->len < b->len)
|
||
|
return -1;
|
||
|
if (a->len > b->len)
|
||
|
return 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
const char *neighbor_ident_addr_name(const struct neighbor_ident_addr *nia)
|
||
|
{
|
||
|
static char buf[128];
|
||
|
struct osmo_strbuf sb = { .buf = buf, .len = sizeof(buf) };
|
||
|
|
||
|
OSMO_STRBUF_PRINTF(sb, "%s-", osmo_rat_type_name(nia->ran_type));
|
||
|
|
||
|
switch (nia->type) {
|
||
|
case MSC_NEIGHBOR_TYPE_LOCAL_RAN_PEER:
|
||
|
OSMO_STRBUF_PRINTF(sb, "localRAN-%s", nia->local_ran_peer_pc_str);
|
||
|
break;
|
||
|
case MSC_NEIGHBOR_TYPE_REMOTE_MSC:
|
||
|
OSMO_STRBUF_PRINTF(sb, "remoteMSC-");
|
||
|
OSMO_STRBUF_APPEND_NOLEN(sb, osmo_escape_str_buf2, nia->remote_msc_ipa_name.buf,
|
||
|
nia->remote_msc_ipa_name.len);
|
||
|
break;
|
||
|
default:
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return buf;
|
||
|
}
|
||
|
|
||
|
const struct neighbor_ident_entry *neighbor_ident_add(struct llist_head *ni_list,
|
||
|
const struct neighbor_ident_addr *nia,
|
||
|
const struct gsm0808_cell_id *cid)
|
||
|
{
|
||
|
struct neighbor_ident_entry *nie;
|
||
|
|
||
|
if (!ni_list)
|
||
|
return NULL;
|
||
|
|
||
|
nie = (struct neighbor_ident_entry*)neighbor_ident_find_by_addr(ni_list, nia);
|
||
|
if (!nie) {
|
||
|
nie = talloc_zero(gsmnet, struct neighbor_ident_entry);
|
||
|
OSMO_ASSERT(nie);
|
||
|
*nie = (struct neighbor_ident_entry){
|
||
|
.addr = *nia,
|
||
|
};
|
||
|
INIT_LLIST_HEAD(&nie->cell_ids);
|
||
|
llist_add_tail(&nie->entry, ni_list);
|
||
|
}
|
||
|
|
||
|
cell_id_list_add_cell(nie, &nie->cell_ids, cid);
|
||
|
return nie;
|
||
|
}
|
||
|
|
||
|
const struct neighbor_ident_entry *neighbor_ident_find_by_cell(const struct llist_head *ni_list,
|
||
|
enum osmo_rat_type ran_type,
|
||
|
const struct gsm0808_cell_id *cell_id)
|
||
|
{
|
||
|
struct neighbor_ident_entry *e;
|
||
|
llist_for_each_entry(e, ni_list, entry) {
|
||
|
if (ran_type != OSMO_RAT_UNKNOWN) {
|
||
|
if (e->addr.ran_type != ran_type)
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (!cell_id_list_find(&e->cell_ids, cell_id, 0, false))
|
||
|
continue;
|
||
|
return e;
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
const struct neighbor_ident_entry *neighbor_ident_find_by_addr(const struct llist_head *ni_list,
|
||
|
const struct neighbor_ident_addr *nia)
|
||
|
{
|
||
|
struct neighbor_ident_entry *e;
|
||
|
|
||
|
llist_for_each_entry(e, ni_list, entry) {
|
||
|
if (nia->ran_type != OSMO_RAT_UNKNOWN
|
||
|
&& e->addr.ran_type != nia->ran_type)
|
||
|
continue;
|
||
|
|
||
|
if (e->addr.type != nia->type)
|
||
|
continue;
|
||
|
|
||
|
switch (e->addr.type) {
|
||
|
case MSC_NEIGHBOR_TYPE_LOCAL_RAN_PEER:
|
||
|
if (strcmp(e->addr.local_ran_peer_pc_str, nia->local_ran_peer_pc_str))
|
||
|
continue;
|
||
|
break;
|
||
|
case MSC_NEIGHBOR_TYPE_REMOTE_MSC:
|
||
|
if (msc_ipa_name_cmp(&e->addr.remote_msc_ipa_name, &nia->remote_msc_ipa_name))
|
||
|
continue;
|
||
|
break;
|
||
|
default:
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
return e;
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void neighbor_ident_del(const struct neighbor_ident_entry *nie)
|
||
|
{
|
||
|
struct neighbor_ident_entry *e = (struct neighbor_ident_entry*)nie;
|
||
|
llist_del(&e->entry);
|
||
|
talloc_free(e);
|
||
|
}
|
||
|
|
||
|
void neighbor_ident_clear(struct llist_head *ni_list)
|
||
|
{
|
||
|
struct neighbor_ident_entry *nie;
|
||
|
while ((nie = llist_first_entry_or_null(ni_list, struct neighbor_ident_entry, entry)))
|
||
|
neighbor_ident_del(nie);
|
||
|
}
|