| /* 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); |
| } |