osmo-msc/src/libmsc/neighbor_ident.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);
}