Projects
osmocom:master
osmo-hnbgw
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 55
View file
osmo-hnbgw.spec
Changed
@@ -18,13 +18,13 @@ Name: osmo-hnbgw Requires: osmocom-master -Version: 1.4.0.86.ee1a +Version: 1.4.0.105.3a084 Release: 0 Summary: OsmoHNBGW: Osmocom's Base Station Controller for 2G CS mobile networks License: AGPL-3.0-or-later AND GPL-2.0-or-later Group: Hardware/Mobile URL: https://osmocom.org/projects/osmohnbgw -Source: osmo-hnbgw_1.4.0.86.ee1a.tar.xz +Source: osmo-hnbgw_1.4.0.105.3a084.tar.xz Source1: rpmlintrc BuildRequires: automake >= 1.9 BuildRequires: libtool >= 2 @@ -96,6 +96,7 @@ %{_docdir}/%{name}/examples/osmo-hnbgw/osmo-hnbgw.cfg %{_docdir}/%{name}/examples/osmo-hnbgw/osmo-hnbgw-cs7.cfg %{_docdir}/%{name}/examples/osmo-hnbgw/osmo-hnbgw-pfcp.cfg +%{_docdir}/%{name}/examples/osmo-hnbgw/osmo-hnbgw-cnpool.cfg %dir %{_sysconfdir}/osmocom %config(noreplace) %{_sysconfdir}/osmocom/osmo-hnbgw.cfg %{_unitdir}/%{name}.service
View file
commit_3a084af8f76c4be4fe67fbe140d340e2e32a7797.txt
Added
View file
commit_ee1a647547fdf5bf57e93254af76276da88c2e94.txt
Deleted
View file
osmo-hnbgw_1.4.0.86.ee1a.dsc -> osmo-hnbgw_1.4.0.105.3a084.dsc
Changed
@@ -2,7 +2,7 @@ Source: osmo-hnbgw Binary: osmo-hnbgw, osmo-hnbgw-dbg, osmo-hnbgw-doc Architecture: any all -Version: 1.4.0.86.ee1a +Version: 1.4.0.105.3a084 Maintainer: Osmocom team <openbsc@lists.osmocom.org> Homepage: https://projects.osmocom.org/projects/osmo-hnbgw Standards-Version: 3.9.8 @@ -14,8 +14,8 @@ osmo-hnbgw-dbg deb debug extra arch=any osmo-hnbgw-doc deb doc optional arch=all Checksums-Sha1: - 8d4ebfa4f7bf8fc8ddb1f3048e5dcbac2032a1cb 88064 osmo-hnbgw_1.4.0.86.ee1a.tar.xz + 7722b6a756c766dfcc0ff6dd02238576df1d8763 96940 osmo-hnbgw_1.4.0.105.3a084.tar.xz Checksums-Sha256: - b6a64b10b0024f8443f275ed49eb061c34faae32fbfd4333f2b7d432ede6f3a2 88064 osmo-hnbgw_1.4.0.86.ee1a.tar.xz + 38dfdfd239f8f7382e73c3ed1a39d344bfbf732ec0e53dd77ab17a37ec1f471d 96940 osmo-hnbgw_1.4.0.105.3a084.tar.xz Files: - 5940e2aaa3cd7dbef97e17e7cac0ac68 88064 osmo-hnbgw_1.4.0.86.ee1a.tar.xz + 6af2fc42fa60258301766a9261e1044e 96940 osmo-hnbgw_1.4.0.105.3a084.tar.xz
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/.tarball-version -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/.tarball-version
Changed
@@ -1 +1 @@ -1.4.0.86-ee1a +1.4.0.105-3a084
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/TODO-RELEASE -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/TODO-RELEASE
Changed
@@ -12,3 +12,4 @@ Uses osmo_scu_prim_hdr_name_c() libosmo-mgcp-client > 1.11.0 mgcp_client_pool_empty(), mgcp_client_conf_alloc() libosmo-sigtran >1.7.0 Require presence of vty 'cs7 instance'/'sccp max-optional-data' that the deprecated+fatal 'hnbgw'/'sccp cr...' tells the user to use instead. +libosmo-ranap >1.4.0 ranap_new_msg_reset2()
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/contrib/osmo-hnbgw.spec.in -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/contrib/osmo-hnbgw.spec.in
Changed
@@ -95,6 +95,7 @@ %{_docdir}/%{name}/examples/osmo-hnbgw/osmo-hnbgw.cfg %{_docdir}/%{name}/examples/osmo-hnbgw/osmo-hnbgw-cs7.cfg %{_docdir}/%{name}/examples/osmo-hnbgw/osmo-hnbgw-pfcp.cfg +%{_docdir}/%{name}/examples/osmo-hnbgw/osmo-hnbgw-cnpool.cfg %dir %{_sysconfdir}/osmocom %config(noreplace) %{_sysconfdir}/osmocom/osmo-hnbgw.cfg %{_unitdir}/%{name}.service
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/debian/changelog -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/debian/changelog
Changed
@@ -1,8 +1,8 @@ -osmo-hnbgw (1.4.0.86.ee1a) unstable; urgency=medium +osmo-hnbgw (1.4.0.105.3a084) unstable; urgency=medium * Automatically generated changelog entry for building the Osmocom master feed - -- Osmocom OBS scripts <info@osmocom.org> Wed, 14 Jun 2023 12:32:57 +0000 + -- Osmocom OBS scripts <info@osmocom.org> Thu, 22 Jun 2023 14:26:57 +0000 osmo-hnbgw (1.4.0) unstable; urgency=medium
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/debian/osmo-hnbgw.install -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/debian/osmo-hnbgw.install
Changed
@@ -4,3 +4,4 @@ usr/share/doc/osmo-hnbgw/examples/osmo-hnbgw/osmo-hnbgw.cfg usr/share/doc/osmo-hnbgw/examples usr/share/doc/osmo-hnbgw/examples/osmo-hnbgw/osmo-hnbgw-pfcp.cfg usr/share/doc/osmo-hnbgw/examples usr/share/doc/osmo-hnbgw/examples/osmo-hnbgw/osmo-hnbgw-cs7.cfg usr/share/doc/osmo-hnbgw/examples +usr/share/doc/osmo-hnbgw/examples/osmo-hnbgw/osmo-hnbgw-cnpool.cfg usr/share/doc/osmo-hnbgw/examples
View file
osmo-hnbgw_1.4.0.105.3a084.tar.xz/doc/examples/osmo-hnbgw/osmo-hnbgw-cnpool.cfg
Added
@@ -0,0 +1,40 @@ +log stderr + logging filter all 1 + logging color 1 + logging print level 1 + logging print category 1 + logging print category-hex 0 + logging print file basename last + logging print extended-timestamp 1 + logging level set-all notice + +cs7 instance 0 + point-code 0.42.0 + + sccp-address my-msc-0 + point-code 0.1.0 + sccp-address my-msc-1 + point-code 0.1.1 + sccp-address my-sgsn-0 + point-code 0.2.0 + +hnbgw + plmn 001 01 + iucs + nri bitlen 10 + nri null add 0 7 + iups + nri bitlen 8 + nri null add 0 5 + +msc 0 + remote-addr my-msc-0 + nri add 128 255 + +msc 1 + remote-addr my-msc-1 + nri add 256 511 + +sgsn 0 + remote-addr my-sgsn-0 + nri add 128 255
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/doc/examples/osmo-hnbgw/osmo-hnbgw-cs7.cfg -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/doc/examples/osmo-hnbgw/osmo-hnbgw-cs7.cfg
Changed
@@ -10,6 +10,7 @@ point-code 1.4.2 hnbgw + plmn 001 01 iuh local-ip 0.0.0.0 hnbap-allow-tmsi 1
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/doc/examples/osmo-hnbgw/osmo-hnbgw-pfcp.cfg -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/doc/examples/osmo-hnbgw/osmo-hnbgw-pfcp.cfg
Changed
@@ -8,6 +8,7 @@ logging print extended-timestamp 1 logging level set-all notice hnbgw + plmn 001 01 iuh local-ip 0.0.0.0 hnbap-allow-tmsi 1
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/doc/examples/osmo-hnbgw/osmo-hnbgw.cfg -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/doc/examples/osmo-hnbgw/osmo-hnbgw.cfg
Changed
@@ -8,6 +8,7 @@ logging print extended-timestamp 1 logging level set-all notice hnbgw + plmn 001 01 iuh local-ip 0.0.0.0 hnbap-allow-tmsi 1
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/include/osmocom/hnbgw/context_map.h -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/include/osmocom/hnbgw/context_map.h
Changed
@@ -3,12 +3,20 @@ #include <stdint.h> #include <osmocom/core/linuxlist.h> #include <osmocom/hnbgw/hnbgw.h> +#include <osmocom/gsm/gsm48.h> #define LOG_MAP(HNB_CTX_MAP, SUBSYS, LEVEL, FMT, ARGS...) \ LOGHNB((HNB_CTX_MAP) ? (HNB_CTX_MAP)->hnb_ctx : NULL, \ - SUBSYS, LEVEL, "RUA-%u %s: " FMT, \ + SUBSYS, LEVEL, "RUA-%u SCCP-%u %s MI=%s%s%s: " FMT, \ (HNB_CTX_MAP) ? (HNB_CTX_MAP)->rua_ctx_id : 0, \ - (HNB_CTX_MAP) ? ((HNB_CTX_MAP)->is_ps ? "PS" : "CS") : "NULL", \ + (HNB_CTX_MAP) ? (HNB_CTX_MAP)->scu_conn_id : 0, \ + (HNB_CTX_MAP) ? \ + ((HNB_CTX_MAP)->cnlink ? (HNB_CTX_MAP)->cnlink->name \ + : ((HNB_CTX_MAP)->is_ps ? "PS" : "CS")) \ + : "NULL", \ + (HNB_CTX_MAP) ? osmo_mobile_identity_to_str_c(OTC_SELECT, &(HNB_CTX_MAP)->l3.mi) : "null", \ + (HNB_CTX_MAP) && (HNB_CTX_MAP)->l3.from_other_plmn ? " (from other PLMN)" : "", \ + (HNB_CTX_MAP) && (HNB_CTX_MAP)->l3.is_emerg ? " EMERGENCY" : "", \ ##ARGS) /* All these events' data argument may either be NULL, or point to a RANAP msgb. @@ -58,6 +66,9 @@ /* The human admin asks to drop the current SCCP connection, by telnet VTY 'apply sccp' in presence of SCCP * config changes. */ MAP_SCCP_EV_USER_ABORT, + /* The CN link can no longer work, for example a RANAP RESET was received from the cnlink that hosts this + * context map. */ + MAP_SCCP_EV_CN_LINK_LOST, }; /* For context_map_get_state(), to combine the RUA and SCCP states, for VTY reporting only. */ @@ -75,6 +86,21 @@ struct hnb_context; struct hnbgw_cnlink; +struct hnbgw_l3_peek { + /* L3 message type, like GSM48_PDISC_MM+GSM48_MT_MM_LOC_UPD_REQUEST... / GSM48_PDISC_MM_GPRS+GSM48_MT_GMM_ATTACH_REQ... */ + uint8_t gsm48_pdisc; + uint8_t gsm48_msg_type; + /* The Mobile Identity from MM and GMM messages */ + struct osmo_mobile_identity mi; + /* On PS, the "TMSI Based NRI Container", 10 bit integer, or -1 if not present. + * This is only for PS -- for CS, the NRI is in the TMSI obtained from 'mi' above. */ + int gmm_nri_container; + /* For a CM Service Request for voice call, true if this is for an Emergency Call, false otherwise. */ + bool is_emerg; + /* True if the NAS PDU indicates that the UE was previously attached to a different PLMN than the local PLMN. */ + bool from_other_plmn; +}; + struct hnbgw_context_map { /* entry in the per-CN list of mappings */ struct llist_head hnbgw_cnlink_entry; @@ -103,6 +129,9 @@ /* False for CS, true for PS */ bool is_ps; + /* Information extracted from RUA Connect's RANAP InitialUE message */ + struct hnbgw_l3_peek l3; + /* When an FSM is asked to disconnect but must still wait for a response, it may set this flag, to continue to * disconnect once the response is in. In particular, when SCCP is asked to disconnect after an SCCP Connection * Request was already sent and while waiting for a Connection Confirmed, we should still wait for the SCCP CC @@ -160,6 +189,7 @@ bool map_rua_is_active(struct hnbgw_context_map *map); bool map_sccp_is_active(struct hnbgw_context_map *map); +void context_map_cnlink_lost(struct hnbgw_context_map *map); void context_map_free(struct hnbgw_context_map *map); unsigned int msg_has_l2_data(const struct msgb *msg);
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/include/osmocom/hnbgw/hnbgw.h -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/include/osmocom/hnbgw/hnbgw.h
Changed
@@ -5,6 +5,8 @@ #include <osmocom/core/hashtable.h> #include <osmocom/core/write_queue.h> #include <osmocom/core/timer.h> +#include <osmocom/core/rate_ctr.h> +#include <osmocom/gsm/gsm23003.h> #include <osmocom/sigtran/sccp_sap.h> #include <osmocom/sigtran/osmo_ss7.h> #include <osmocom/ctrl/control_if.h> @@ -35,6 +37,12 @@ #define DOMAIN_CS RANAP_CN_DomainIndicator_cs_domain #define DOMAIN_PS RANAP_CN_DomainIndicator_ps_domain +extern const struct value_string ranap_domain_names; +static inline const char *ranap_domain_name(RANAP_CN_DomainIndicator_t domain) +{ + return get_value_string(ranap_domain_names, domain); +} + enum hnb_ctrl_node { CTRL_NODE_HNB = _LAST_CTRL_NODE, _LAST_CTRL_NODE_HNB @@ -70,6 +78,7 @@ uint16_t sac; /*!< Service Area Code */ uint32_t cid; /*!< Cell ID */ }; +const char *umts_cell_id_name(const struct umts_cell_id *ucid); struct hnbgw_context_map; @@ -135,6 +144,12 @@ /* Emergency calls potentially select a different set of MSCs, so to not mess up the normal round-robin * behavior, emergency calls need a separate round-robin counter. */ unsigned int round_robin_next_emerg_nr; + + /* rate counter group that child hnbgw_cnlinks should use (points to msc_ctrg_desc or sgsn_ctrg_desc) */ + const struct rate_ctr_group_desc *cnlink_ctrg_desc; + + /* Running counters for this pool */ + struct rate_ctr_group *ctrs; }; /* A CN peer, like 'msc 0' or 'sgsn 23' */ @@ -144,6 +159,8 @@ /* backpointer to CS or PS CN pool. */ struct hnbgw_cnpool *pool; + struct osmo_fsm_inst *fi; + int nr; struct hnbgw_cnlink_cfg vty; @@ -164,6 +181,9 @@ bool allow_attach; bool allow_emerg; + struct llist_head paging; + + struct rate_ctr_group *ctrs; }; #define LOG_CNLINK(CNLINK, SUBSYS, LEVEL, FMT, ARGS...) \ @@ -234,6 +254,7 @@ /*! The UDP port where we receive multiplexed CS user * plane traffic from HNBs */ uint16_t iuh_cs_mux_port; + struct osmo_plmn_id plmn; uint16_t rnc_id; bool hnbap_allow_tmsi; /*! print hnb-id (true) or MCC-MNC-LAC-RAC-SAC (false) in logs */ @@ -312,3 +333,5 @@ } struct msgb *hnbgw_ranap_msg_alloc(const char *name); + +int hnbgw_peek_l3(struct hnbgw_context_map *map, struct msgb *ranap_msg);
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/include/osmocom/hnbgw/hnbgw_cn.h -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/include/osmocom/hnbgw/hnbgw_cn.h
Changed
@@ -1,7 +1,9 @@ #pragma once +#include <osmocom/core/rate_ctr.h> #include <osmocom/hnbgw/hnbgw.h> +struct hnbgw_cnlink *cnlink_alloc(struct hnbgw_cnpool *cnpool, int nr); struct hnbgw_cnlink *hnbgw_cnlink_find_by_addr(const struct hnbgw_sccp_user *hsu, const struct osmo_sccp_addr *remote_addr); struct hnbgw_cnlink *hnbgw_cnlink_select(struct hnbgw_context_map *map); @@ -13,3 +15,39 @@ char *cnlink_sccp_addr_to_str(struct hnbgw_cnlink *cnlink, const struct osmo_sccp_addr *addr); +bool cnlink_is_conn_ready(const struct hnbgw_cnlink *cnlink); +void cnlink_rx_reset_cmd(struct hnbgw_cnlink *cnlink); +void cnlink_rx_reset_ack(struct hnbgw_cnlink *cnlink); +void cnlink_resend_reset(struct hnbgw_cnlink *cnlink); +void cnlink_set_disconnected(struct hnbgw_cnlink *cnlink); + +enum hnbgw_cnpool_ctr { + /* TODO: basic counters completely missing + * ... + */ + + /* Counters related to link selection from a CN pool. */ + CNPOOL_CTR_SUBSCR_NO_CNLINK, + CNPOOL_CTR_EMERG_FORWARDED, + CNPOOL_CTR_EMERG_LOST, +}; + +extern const struct rate_ctr_group_desc iucs_ctrg_desc; +extern const struct rate_ctr_group_desc iups_ctrg_desc; + +enum hnbgw_cnlink_ctr { + /* TODO: basic counters completely missing + * ... + */ + + /* Counters related to link selection from a CN pool. */ + CNLINK_CTR_CNPOOL_SUBSCR_NEW, + CNLINK_CTR_CNPOOL_SUBSCR_REATTACH, + CNLINK_CTR_CNPOOL_SUBSCR_KNOWN, + CNLINK_CTR_CNPOOL_SUBSCR_PAGED, + CNLINK_CTR_CNPOOL_SUBSCR_ATTACH_LOST, + CNLINK_CTR_CNPOOL_EMERG_FORWARDED, +}; + +extern const struct rate_ctr_group_desc msc_ctrg_desc; +extern const struct rate_ctr_group_desc sgsn_ctrg_desc;
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/src/osmo-hnbgw/Makefile.am -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/src/osmo-hnbgw/Makefile.am
Changed
@@ -33,6 +33,7 @@ libhnbgw_la_SOURCES = \ hnbgw.c \ hnbgw_hnbap.c \ + hnbgw_l3.c \ hnbgw_rua.c \ hnbgw_ranap.c \ hnbgw_vty.c \ @@ -40,6 +41,7 @@ context_map_rua.c \ context_map_sccp.c \ hnbgw_cn.c \ + cnlink.c \ ranap_rab_ass.c \ mgw_fsm.c \ tdefs.c \
View file
osmo-hnbgw_1.4.0.105.3a084.tar.xz/src/osmo-hnbgw/cnlink.c
Added
@@ -0,0 +1,374 @@ +/* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * + * Author: Neels Hofmeyr + * + * 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 <osmocom/core/fsm.h> +#include <osmocom/core/tdef.h> + +#include <osmocom/gsm/gsm23236.h> + +#include <osmocom/sigtran/sccp_helpers.h> + +#include <asn1c/asn1helpers.h> +#include <osmocom/ranap/ranap_ies_defs.h> +#include <osmocom/ranap/ranap_msg_factory.h> + +#include <osmocom/hnbgw/hnbgw.h> +#include <osmocom/hnbgw/hnbgw_cn.h> +#include <osmocom/hnbgw/tdefs.h> +#include <osmocom/hnbgw/context_map.h> + +static struct osmo_fsm cnlink_fsm; + +enum cnlink_fsm_state { + CNLINK_ST_DISC, + CNLINK_ST_CONN, +}; + +enum cnlink_fsm_event { + CNLINK_EV_RX_RESET, + CNLINK_EV_RX_RESET_ACK, +}; + +static const struct value_string cnlink_fsm_event_names = { + OSMO_VALUE_STRING(CNLINK_EV_RX_RESET), + OSMO_VALUE_STRING(CNLINK_EV_RX_RESET_ACK), + {} +}; + +static const struct osmo_tdef_state_timeout cnlink_timeouts32 = { + CNLINK_ST_DISC = { .T = 4 }, +}; + +#define cnlink_fsm_state_chg(FI, STATE) \ + osmo_tdef_fsm_inst_state_chg(FI, STATE, \ + cnlink_timeouts, \ + hnbgw_T_defs, \ + -1) + +struct hnbgw_cnlink *cnlink_alloc(struct hnbgw_cnpool *cnpool, int nr) +{ + struct osmo_fsm_inst *fi; + struct hnbgw_cnlink *cnlink; + + char *name = talloc_asprintf(OTC_SELECT, "%s-%d", cnpool->peer_name, nr); + + fi = osmo_fsm_inst_alloc(&cnlink_fsm, g_hnbgw, NULL, LOGL_DEBUG, name); + OSMO_ASSERT(fi); + cnlink = talloc_zero(g_hnbgw, struct hnbgw_cnlink); + fi->priv = cnlink; + + *cnlink = (struct hnbgw_cnlink){ + .name = name, + .pool = cnpool, + .fi = fi, + .nr = nr, + .vty = { + /* VTY config defaults for the new cnlink */ + .nri_ranges = osmo_nri_ranges_alloc(cnlink), + }, + .allow_attach = true, + .ctrs = rate_ctr_group_alloc(g_hnbgw, cnpool->cnlink_ctrg_desc, nr), + }; + talloc_steal(cnlink, name); + INIT_LLIST_HEAD(&cnlink->map_list); + INIT_LLIST_HEAD(&cnlink->paging); + + llist_add_tail(&cnlink->entry, &cnpool->cnlinks); + LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "allocated\n"); + + /* Immediately (1ms) kick off reset sending mechanism */ + osmo_fsm_inst_state_chg_ms(fi, CNLINK_ST_DISC, 1, 0); + return cnlink; +} + +void cnlink_term_and_free(struct hnbgw_cnlink *cnlink) +{ + if (!cnlink) + return; + osmo_fsm_inst_term(cnlink->fi, OSMO_FSM_TERM_REQUEST, NULL); + talloc_free(cnlink); +} + +static void link_up(struct hnbgw_cnlink *cnlink) +{ + LOGPFSML(cnlink->fi, LOGL_NOTICE, "link up\n"); +} + +static void link_lost(struct hnbgw_cnlink *cnlink) +{ + struct hnbgw_context_map *map, *map2; + + LOGPFSML(cnlink->fi, LOGL_NOTICE, "link lost\n"); + + llist_for_each_entry_safe(map, map2, &cnlink->map_list, hnbgw_cnlink_entry) + context_map_cnlink_lost(map); +} + +static void tx_reset(struct hnbgw_cnlink *cnlink) +{ + struct msgb *msg; + RANAP_Cause_t cause = { + .present = RANAP_Cause_PR_transmissionNetwork, + .choice. transmissionNetwork = RANAP_CauseTransmissionNetwork_signalling_transport_resource_failure, + }; + RANAP_GlobalRNC_ID_t grnc_id; + RANAP_GlobalRNC_ID_t *use_grnc_id = NULL; + uint8_t plmn_buf3; + + if (!cnlink) + return; + + /* We need to have chosen an SCCP instance, and the remote SCCP address needs to be set. + * Only check the remote_addr, allowing use.remote_addr_name to be NULL: if the user has not set an explicit + * remote address book entry, auto-configuration may still have chosen a default remote point-code. */ + if (!cnlink->hnbgw_sccp_user + || !osmo_sccp_check_addr(&cnlink->remote_addr, OSMO_SCCP_ADDR_T_PC | OSMO_SCCP_ADDR_T_SSN)) { + LOG_CNLINK(cnlink, DRANAP, LOGL_DEBUG, "not yet configured, not sending RANAP RESET\n"); + return; + } + + LOG_CNLINK(cnlink, DRANAP, LOGL_DEBUG, "Tx RANAP RESET to %s %s\n", + cnlink_is_cs(cnlink) ? "IuCS" : "IuPS", + cnlink_sccp_addr_to_str(cnlink, &cnlink->remote_addr)); + + if (g_hnbgw->config.plmn.mcc) { + osmo_plmn_to_bcd(plmn_buf, &g_hnbgw->config.plmn); + grnc_id = (RANAP_GlobalRNC_ID_t){ + .pLMNidentity = { + .buf = plmn_buf, + .size = 3, + }, + .rNC_ID = g_hnbgw->config.rnc_id, + }; + use_grnc_id = &grnc_id; + } else { + /* If no PLMN is configured, omit the Global RNC Id from the RESET message. + * + * According to 3GPP TS 25.413 8.26.2.2, "The RNC shall include the Global RNC-ID IE in the RESET + * message", so it should be considered a mandatory IE when coming from us, the RNC. + * + * But osmo-hnbgw < v1.5 worked well with osmo-hnbgw.cfg files that have no PLMN configured, and we are + * trying to stay backwards compatible for those users. Such a site should still work, but they should + * now see these error logs and can adjust the config. + */ + LOG_CNLINK(cnlink, DRANAP, LOGL_ERROR, + "No local PLMN is configured, so outgoing RESET messages omit the mandatory Global RNC-ID" + " IE. You should set a 'hnbgw' / 'plmn' in your config file (since v1.5)\n"); + } + + msg = ranap_new_msg_reset2(cnlink->pool->domain, &cause, use_grnc_id); + + osmo_sccp_tx_unitdata_msg(cnlink->hnbgw_sccp_user->sccp_user, + &cnlink->hnbgw_sccp_user->local_addr, + &cnlink->remote_addr, + msg); +} + +static void tx_reset_ack(struct hnbgw_cnlink *cnlink) +{ + struct msgb *msg; + struct osmo_sccp_instance *sccp = cnlink_sccp(cnlink); + RANAP_GlobalRNC_ID_t grnc_id; + RANAP_GlobalRNC_ID_t *use_grnc_id = NULL; + uint8_t plmn_buf3; + + if (!sccp) { + LOG_CNLINK(cnlink, DRANAP, LOGL_ERROR, "cannot send RANAP RESET ACK: no CN link\n"); + return; + } + + LOG_CNLINK(cnlink, DRANAP, LOGL_NOTICE, "Tx RANAP RESET ACK %s %s --> %s\n", + cnlink_is_cs(cnlink) ? "IuCS" : "IuPS", + cnlink_sccp_addr_to_str(cnlink, &cnlink->hnbgw_sccp_user->local_addr), + cnlink_sccp_addr_to_str(cnlink, &cnlink->remote_addr)); + + if (g_hnbgw->config.plmn.mcc) { + osmo_plmn_to_bcd(plmn_buf, &g_hnbgw->config.plmn); + grnc_id = (RANAP_GlobalRNC_ID_t){ + .pLMNidentity = { + .buf = plmn_buf, + .size = 3, + }, + .rNC_ID = g_hnbgw->config.rnc_id, + }; + use_grnc_id = &grnc_id; + } else { + /* If no PLMN is configured, omit the Global RNC Id from the RESET ACK message. + * + * According to 3GPP TS 25.413 8.26.2.1, "The RNC shall include the Global RNC-ID IE in the RESET + * ACKNOWLEDGE message", so it should be considered a mandatory IE when coming from us, the RNC. + * + * But osmo-hnbgw < v1.5 worked well with osmo-hnbgw.cfg files that have no PLMN configured, and we are + * trying to stay backwards compatible for those users. Such a site should still work, but they should + * now see these error logs and can adjust the config. + */ + LOG_CNLINK(cnlink, DRANAP, LOGL_ERROR, + "No local PLMN is configured, so outgoing RESET ACKNOWLEDGE messages omit the mandatory" + " Global RNC-ID IE. You should set a 'hnbgw' / 'plmn' in your config file (since v1.5)\n"); + } + + msg = ranap_new_msg_reset_ack(cnlink->pool->domain, use_grnc_id); + + osmo_sccp_tx_unitdata_msg(cnlink->hnbgw_sccp_user->sccp_user, + &cnlink->hnbgw_sccp_user->local_addr, + &cnlink->remote_addr, + msg); +} + +static void cnlink_disc_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv; + if (prev_state == CNLINK_ST_CONN) + link_lost(cnlink); +} + +static void cnlink_disc_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv; + switch (event) { + + case CNLINK_EV_RX_RESET: + tx_reset_ack(cnlink); + cnlink_fsm_state_chg(fi, CNLINK_ST_CONN); + break; + + case CNLINK_EV_RX_RESET_ACK: + cnlink_fsm_state_chg(fi, CNLINK_ST_CONN); + break; + + default: + OSMO_ASSERT(false); + } +} + +static void cnlink_conn_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv; + if (prev_state != CNLINK_ST_CONN) + link_up(cnlink); +} + +static void cnlink_conn_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv; + + switch (event) { + + case CNLINK_EV_RX_RESET: + /* We were connected, but the remote side has restarted. */ + link_lost(cnlink); + tx_reset_ack(cnlink); + link_up(cnlink); + break; + + case CNLINK_EV_RX_RESET_ACK: + LOGPFSML(fi, LOGL_INFO, "Link is already up, ignoring RESET ACK\n"); + break; + + default: + OSMO_ASSERT(false); + } +} + +static int cnlink_fsm_timer_cb(struct osmo_fsm_inst *fi) +{ + struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv; + + tx_reset(cnlink); + + /* (re-)enter disconnect state to resend RESET after timeout. */ + cnlink_fsm_state_chg(fi, CNLINK_ST_DISC); + + /* Return 0 to not terminate the fsm */ + return 0; +} + +#define S(x) (1 << (x)) + +static struct osmo_fsm_state cnlink_fsm_states = { + CNLINK_ST_DISC = { + .name = "DISC", + .in_event_mask = 0 + | S(CNLINK_EV_RX_RESET) + | S(CNLINK_EV_RX_RESET_ACK) + , + .out_state_mask = 0 + | S(CNLINK_ST_DISC) + | S(CNLINK_ST_CONN) + , + .onenter = cnlink_disc_onenter, + .action = cnlink_disc_action, + }, + CNLINK_ST_CONN = { + .name = "CONN", + .in_event_mask = 0 + | S(CNLINK_EV_RX_RESET) + | S(CNLINK_EV_RX_RESET_ACK) + , + .out_state_mask = 0 + | S(CNLINK_ST_DISC) + | S(CNLINK_ST_CONN) + , + .onenter = cnlink_conn_onenter, + .action = cnlink_conn_action, + }, +}; + +static struct osmo_fsm cnlink_fsm = { + .name = "cnlink", + .states = cnlink_fsm_states, + .num_states = ARRAY_SIZE(cnlink_fsm_states), + .log_subsys = DRANAP, + .timer_cb = cnlink_fsm_timer_cb, + .event_names = cnlink_fsm_event_names, +}; + +bool cnlink_is_conn_ready(const struct hnbgw_cnlink *cnlink) +{ + return cnlink->fi->state == CNLINK_ST_CONN; +} + +void cnlink_resend_reset(struct hnbgw_cnlink *cnlink) +{ + /* Immediately (1ms) kick off reset sending mechanism */ + osmo_fsm_inst_state_chg_ms(cnlink->fi, CNLINK_ST_DISC, 1, 0); +} + +void cnlink_set_disconnected(struct hnbgw_cnlink *cnlink) +{ + /* Go to disconnected state, with the normal RESET timeout to re-send RESET. */ + cnlink_fsm_state_chg(cnlink->fi, CNLINK_ST_DISC); +} + +static __attribute__((constructor)) void cnlink_fsm_init(void) +{ + OSMO_ASSERT(osmo_fsm_register(&cnlink_fsm) == 0); +} + +void cnlink_rx_reset_cmd(struct hnbgw_cnlink *cnlink) +{ + osmo_fsm_inst_dispatch(cnlink->fi, CNLINK_EV_RX_RESET, NULL); +} + +void cnlink_rx_reset_ack(struct hnbgw_cnlink *cnlink) +{ + osmo_fsm_inst_dispatch(cnlink->fi, CNLINK_EV_RX_RESET_ACK, NULL); +} +
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/src/osmo-hnbgw/context_map.c -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/src/osmo-hnbgw/context_map.c
Changed
@@ -26,6 +26,8 @@ #include <osmocom/core/timer.h> +#include <osmocom/netif/stream.h> + #include <osmocom/sigtran/sccp_helpers.h> #include <osmocom/hnbgw/hnbgw_cn.h> @@ -92,7 +94,7 @@ llist_add_tail(&map->hnb_list, &hnb->map_list); - LOG_MAP(map, DRUA, LOGL_INFO, "New RUA CTX\n"); + LOG_MAP(map, DRUA, LOGL_DEBUG, "New RUA CTX\n"); return map; } @@ -123,8 +125,18 @@ hash_add(hsu->hnbgw_context_map_by_conn_id, &map->hnbgw_sccp_user_entry, new_scu_conn_id); - LOG_MAP(map, DMAIN, LOGL_INFO, "Creating new Mapping RUA CTX %u <-> SCU Conn ID %u to %s on %s\n", - map->rua_ctx_id, new_scu_conn_id, cnlink_selected->name, hsu->name); + LOGP(DRUA, LOGL_NOTICE, "New conn: %s '%s' RUA-%u <-> SCCP-%u %s%s%s %s l=%s<->r=%s\n", + osmo_sock_get_name2_c(OTC_SELECT, osmo_stream_srv_get_ofd(map->hnb_ctx->conn)->fd), + hnb_context_name(map->hnb_ctx), map->rua_ctx_id, + new_scu_conn_id, + cnlink_selected->name, + cnlink_selected->use.remote_addr_name ? " " : "", + cnlink_selected->use.remote_addr_name ? : "", + hsu->name, + /* printing the entire SCCP address is quite long, rather just print the point-code */ + osmo_ss7_pointcode_print(hsu->ss7, hsu->local_addr.pc), + osmo_ss7_pointcode_print2(hsu->ss7, cnlink_selected->remote_addr.pc) + ); return 0; } @@ -172,6 +184,11 @@ map_rua_dispatch(map, MAP_RUA_EV_HNB_LINK_LOST, NULL); } +void context_map_cnlink_lost(struct hnbgw_context_map *map) +{ + map_sccp_dispatch(map, MAP_SCCP_EV_RAN_LINK_LOST, NULL); +} + void context_map_free(struct hnbgw_context_map *map) { /* guard against FSM termination infinitely looping back here */
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/src/osmo-hnbgw/context_map_rua.c -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/src/osmo-hnbgw/context_map_rua.c
Changed
@@ -262,6 +262,7 @@ .present = RUA_Cause_PR_radioNetwork, .choice.radioNetwork = RUA_CauseRadioNetwork_network_release, }; + LOGPFSML(fi, LOGL_INFO, "Tx RUA Disconnect\n"); if (rua_tx_disc(map->hnb_ctx, map->is_ps, map->rua_ctx_id, &rua_cause, NULL, 0)) LOGPFSML(fi, LOGL_ERROR, "Failed to send Disconnect to RUA\n"); }
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/src/osmo-hnbgw/context_map_sccp.c -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/src/osmo-hnbgw/context_map_sccp.c
Changed
@@ -55,6 +55,7 @@ OSMO_VALUE_STRING(MAP_SCCP_EV_RAN_LINK_LOST), OSMO_VALUE_STRING(MAP_SCCP_EV_RX_RELEASED), OSMO_VALUE_STRING(MAP_SCCP_EV_USER_ABORT), + OSMO_VALUE_STRING(MAP_SCCP_EV_CN_LINK_LOST), {} }; @@ -81,7 +82,8 @@ struct osmo_fsm_inst *fi = osmo_fsm_inst_alloc(&map_sccp_fsm, map, map, LOGL_DEBUG, NULL); OSMO_ASSERT(fi); osmo_fsm_inst_update_id_f_sanitize(fi, '-', "%s-%s-SCCP-%u", hnb_context_name(map->hnb_ctx), - map->is_ps ? "PS" : "CS", map->scu_conn_id); + map->cnlink ? map->cnlink->name : (map->is_ps ? "PS" : "CS"), + map->scu_conn_id); OSMO_ASSERT(map->sccp_fi == NULL); map->sccp_fi = fi; @@ -283,6 +285,7 @@ case MAP_SCCP_EV_RAN_LINK_LOST: case MAP_SCCP_EV_RAN_DISC: case MAP_SCCP_EV_USER_ABORT: + case MAP_SCCP_EV_CN_LINK_LOST: /* No CR has been sent yet, just go to disconnected state. */ if (msg_has_l2_data(ranap_msg)) LOG_MAP(map, DLSCCP, LOGL_ERROR, "SCCP not connected, cannot dispatch RANAP message\n"); @@ -320,6 +323,7 @@ case MAP_SCCP_EV_RAN_LINK_LOST: case MAP_SCCP_EV_RAN_DISC: case MAP_SCCP_EV_USER_ABORT: + case MAP_SCCP_EV_CN_LINK_LOST: /* RUA connection was terminated. First wait for the CC before releasing the SCCP conn. */ if (msg_has_l2_data(ranap_msg)) LOGPFSML(fi, LOGL_ERROR, "Connection not yet confirmed, cannot forward RANAP to CN\n"); @@ -378,6 +382,8 @@ * Disconnect on the SCCP layer, ungracefully. */ case MAP_SCCP_EV_USER_ABORT: /* The user is asking for disconnection, so there is no Iu Release in progress. Disconnect now. */ + case MAP_SCCP_EV_CN_LINK_LOST: + /* The CN peer has sent a RANAP RESET, so the old link that this map ran on is lost */ /* There won't be any ranap_msg, but if a caller wants to dispatch a msg, forward it before * disconnecting. */ @@ -447,6 +453,7 @@ return; case MAP_SCCP_EV_USER_ABORT: + case MAP_SCCP_EV_CN_LINK_LOST: /* Stop waiting for RLSD, send RLSD now. */ tx_sccp_rlsd(fi); map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED); @@ -524,6 +531,7 @@ | S(MAP_SCCP_EV_RAN_LINK_LOST) | S(MAP_SCCP_EV_RX_RELEASED) | S(MAP_SCCP_EV_USER_ABORT) + | S(MAP_SCCP_EV_CN_LINK_LOST) , .out_state_mask = 0 | S(MAP_SCCP_ST_INIT) @@ -541,6 +549,7 @@ | S(MAP_SCCP_EV_RAN_LINK_LOST) | S(MAP_SCCP_EV_RX_RELEASED) | S(MAP_SCCP_EV_USER_ABORT) + | S(MAP_SCCP_EV_CN_LINK_LOST) , .out_state_mask = 0 | S(MAP_SCCP_ST_CONNECTED) @@ -558,6 +567,7 @@ | S(MAP_SCCP_EV_RX_RELEASED) | S(MAP_SCCP_EV_RX_CONNECTION_CONFIRM) | S(MAP_SCCP_EV_USER_ABORT) + | S(MAP_SCCP_EV_CN_LINK_LOST) , .out_state_mask = 0 | S(MAP_SCCP_ST_WAIT_RLSD) @@ -576,6 +586,7 @@ | S(MAP_SCCP_EV_RAN_LINK_LOST) | S(MAP_SCCP_EV_RX_CONNECTION_CONFIRM) | S(MAP_SCCP_EV_USER_ABORT) + | S(MAP_SCCP_EV_CN_LINK_LOST) , .out_state_mask = 0 | S(MAP_SCCP_ST_DISCONNECTED) @@ -590,6 +601,7 @@ | S(MAP_SCCP_EV_RAN_DISC) | S(MAP_SCCP_EV_RAN_LINK_LOST) | S(MAP_SCCP_EV_USER_ABORT) + | S(MAP_SCCP_EV_CN_LINK_LOST) , .onenter = map_sccp_disconnected_onenter, .action = map_sccp_disconnected_action,
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/src/osmo-hnbgw/hnbgw.c -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/src/osmo-hnbgw/hnbgw.c
Changed
@@ -36,10 +36,17 @@ #include <osmocom/hnbgw/hnbgw.h> #include <osmocom/hnbgw/hnbgw_hnbap.h> #include <osmocom/hnbgw/hnbgw_rua.h> +#include <osmocom/hnbgw/hnbgw_cn.h> #include <osmocom/hnbgw/context_map.h> struct hnbgw *g_hnbgw = NULL; +const struct value_string ranap_domain_names = { + { DOMAIN_CS, "CS" }, + { DOMAIN_PS, "PS" }, + {} +}; + void g_hnbgw_alloc(void *ctx) { OSMO_ASSERT(!g_hnbgw); @@ -50,6 +57,9 @@ g_hnbgw->config.iuh_local_port = IUH_DEFAULT_SCTP_PORT; g_hnbgw->config.log_prefix_hnb_id = true; + /* Set zero PLMN to detect a missing PLMN when transmitting RESET */ + g_hnbgw->config.plmn = (struct osmo_plmn_id){ 0, 0, false }; + g_hnbgw->next_ue_ctx_id = 23; INIT_LLIST_HEAD(&g_hnbgw->hnb_list); INIT_LLIST_HEAD(&g_hnbgw->ue_list); @@ -71,6 +81,9 @@ .nri_bitlen = OSMO_NRI_BITLEN_DEFAULT, .null_nri_ranges = osmo_nri_ranges_alloc(g_hnbgw), }, + .cnlink_ctrg_desc = &msc_ctrg_desc, + + .ctrs = rate_ctr_group_alloc(g_hnbgw, &iucs_ctrg_desc, 0), }; INIT_LLIST_HEAD(&g_hnbgw->sccp.cnpool_iucs.cnlinks); @@ -83,6 +96,9 @@ .nri_bitlen = OSMO_NRI_BITLEN_DEFAULT, .null_nri_ranges = osmo_nri_ranges_alloc(g_hnbgw), }, + .cnlink_ctrg_desc = &sgsn_ctrg_desc, + + .ctrs = rate_ctr_group_alloc(g_hnbgw, &iups_ctrg_desc, 0), }; INIT_LLIST_HEAD(&g_hnbgw->sccp.cnpool_iups.cnlinks); } @@ -335,23 +351,37 @@ return ctx; } -static const char *umts_cell_id_name(const struct umts_cell_id *ucid) +const char *umts_cell_id_name(const struct umts_cell_id *ucid) { - static __thread char buf40; - - snprintf(buf, sizeof(buf), "%u-%u-L%u-R%u-S%u", ucid->mcc, ucid->mnc, ucid->lac, ucid->rac, ucid->sac); - return buf; + return talloc_asprintf(OTC_SELECT, "%u-%u-L%u-R%u-S%u-C%u", ucid->mcc, ucid->mnc, ucid->lac, ucid->rac, + ucid->sac, ucid->cid); } const char *hnb_context_name(struct hnb_context *ctx) { + char *result; if (!ctx) return "NULL"; + if (ctx->conn) { + char hostbuf_rINET6_ADDRSTRLEN; + char portbuf_r6; + int fd = osmo_stream_srv_get_ofd(ctx->conn)->fd; + + /* get remote addr */ + if (osmo_sock_get_ip_and_port(fd, hostbuf_r, sizeof(hostbuf_r), portbuf_r, sizeof(portbuf_r), false) == 0) + result = talloc_asprintf(OTC_SELECT, "%s:%s", hostbuf_r, portbuf_r); + else + result = "?"; + } else { + result = "disconnected"; + } + if (g_hnbgw->config.log_prefix_hnb_id) - return ctx->identity_info; + result = talloc_asprintf(OTC_SELECT, "%s %s", result, ctx->identity_info); else - return umts_cell_id_name(&ctx->id); + result = talloc_asprintf(OTC_SELECT, "%s %s", result, umts_cell_id_name(&ctx->id)); + return result; } void hnb_context_release_ue_state(struct hnb_context *ctx)
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/src/osmo-hnbgw/hnbgw_cn.c -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/src/osmo-hnbgw/hnbgw_cn.c
Changed
@@ -23,9 +23,12 @@ #include <arpa/inet.h> #include <errno.h> +#include <asn1c/asn1helpers.h> + #include <osmocom/core/msgb.h> #include <osmocom/core/utils.h> #include <osmocom/core/timer.h> +#include <osmocom/core/stats.h> #include <osmocom/gsm/gsm23236.h> @@ -36,128 +39,275 @@ #include <osmocom/hnbgw/hnbgw.h> #include <osmocom/hnbgw/hnbgw_rua.h> #include <osmocom/hnbgw/hnbgw_cn.h> +#include <osmocom/hnbgw/tdefs.h> #include <osmocom/ranap/ranap_ies_defs.h> #include <osmocom/ranap/ranap_msg_factory.h> +#include <osmocom/ranap/iu_helpers.h> #include <osmocom/hnbgw/context_map.h> -#if 0 -this code will soon move to new file cnlink.c -static int transmit_rst(struct hnbgw_cnlink *cnlink) -{ - struct msgb *msg; - RANAP_Cause_t cause = { - .present = RANAP_Cause_PR_transmissionNetwork, - .choice. transmissionNetwork = RANAP_CauseTransmissionNetwork_signalling_transport_resource_failure, - }; +/*********************************************************************** + * Incoming primitives from SCCP User SAP + ***********************************************************************/ - if (!cnlink) +static int cn_ranap_rx_reset_cmd(struct hnbgw_cnlink *cnlink, + const struct osmo_scu_unitdata_param *unitdata, + RANAP_InitiatingMessage_t *imsg) +{ + RANAP_CN_DomainIndicator_t domain; + RANAP_ResetIEs_t ies; + int rc; + + rc = ranap_decode_reseties(&ies, &imsg->value); + domain = ies.cN_DomainIndicator; + ranap_free_reseties(&ies); + + if (rc) { + LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "Rx RESET: cannot decode IEs\n"); return -1; + } - if (!cnlink->hnbgw_sccp_user) { - LOG_CNLINK(cnlink, DRANAP, LOGL_ERROR, "cannot send RANAP RESET: no CN link\n"); + if (cnlink->pool->domain != domain) { + LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "Rx RESET indicates domain %s, but this is %s on domain %s\n", + ranap_domain_name(domain), cnlink->name, ranap_domain_name(cnlink->pool->domain)); return -1; } - LOG_CNLINK(cnlink, DRANAP, LOGL_NOTICE, "Tx RANAP RESET to %s %s\n", - cnlink_is_cs(cnlink) ? "IuCS" : "IuPS", - osmo_sccp_inst_addr_name(cnlink->hnbgw_sccp_user->sccp, &cnlink->remote_addr)); + cnlink_rx_reset_cmd(cnlink); + return 0; +} - msg = ranap_new_msg_reset(cnlink->pool->domain, &cause); +static int cn_ranap_rx_reset_ack(struct hnbgw_cnlink *cnlink, + RANAP_SuccessfulOutcome_t *omsg) +{ + RANAP_CN_DomainIndicator_t domain; + RANAP_ResetAcknowledgeIEs_t ies; + int rc; + + rc = ranap_decode_resetacknowledgeies(&ies, &omsg->value); + domain = ies.cN_DomainIndicator; + ranap_free_resetacknowledgeies(&ies); + + if (rc) { + LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "Rx RESET ACK: cannot decode IEs\n"); + return -1; + } + + if (cnlink->pool->domain != domain) { + LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "Rx RESET ACK indicates domain %s, but this is %s on domain %s\n", + ranap_domain_name(domain), cnlink->name, ranap_domain_name(cnlink->pool->domain)); + return -1; + } - return osmo_sccp_tx_unitdata_msg(cnlink->hnbgw_sccp_user->sccp_user, - &cnlink->local_addr, - &cnlink->remote_addr, - msg); + cnlink_rx_reset_ack(cnlink); + return 0; } -#endif -static int transmit_reset_ack(struct hnbgw_cnlink *cnlink) +struct cnlink_paging { + struct llist_head entry; + + struct osmo_mobile_identity mi; + struct osmo_mobile_identity mi2; + time_t timestamp; +}; + +static int cnlink_paging_destructor(struct cnlink_paging *p) { - struct msgb *msg; - struct osmo_sccp_instance *sccp = cnlink_sccp(cnlink); + llist_del(&p->entry); + return 0; +} - if (!sccp) { - LOG_CNLINK(cnlink, DRANAP, LOGL_ERROR, "cannot send RANAP RESET ACK: no CN link\n"); - return -1; +/* Return current timestamp in *timestamp, and the oldest still valid timestamp according to T3113 timeout. */ +static const char *cnlink_paging_gettime(time_t *timestamp_p, time_t *timeout_p) +{ + struct timespec now; + time_t timestamp; + + /* get timestamp */ + if (osmo_clock_gettime(CLOCK_MONOTONIC, &now) != 0) + return "cannot get timestamp"; + timestamp = now.tv_sec; + + if (timestamp_p) + *timestamp_p = timestamp; + if (timeout_p) + *timeout_p = timestamp - osmo_tdef_get(hnbgw_T_defs, 3113, OSMO_TDEF_S, 15); + return NULL; +} + +static const char *cnlink_paging_add(struct hnbgw_cnlink *cnlink, const struct osmo_mobile_identity *mi, + const struct osmo_mobile_identity *mi2) +{ + struct cnlink_paging *p, *p2; + time_t timestamp; + time_t timeout; + const char *errmsg; + + errmsg = cnlink_paging_gettime(×tamp, &timeout); + if (errmsg) + return errmsg; + + /* Prune all paging records that are older than the configured timeout. */ + llist_for_each_entry_safe(p, p2, &cnlink->paging, entry) { + if (p->timestamp >= timeout) + continue; + talloc_free(p); } - LOG_CNLINK(cnlink, DRANAP, LOGL_NOTICE, "Tx RANAP RESET ACK %s %s --> %s\n", - cnlink_is_cs(cnlink) ? "IuCS" : "IuPS", - cnlink_sccp_addr_to_str(cnlink, &cnlink->hnbgw_sccp_user->local_addr), - cnlink_sccp_addr_to_str(cnlink, &cnlink->remote_addr)); + /* Add new entry */ + p = talloc_zero(cnlink, struct cnlink_paging); + *p = (struct cnlink_paging){ + .timestamp = timestamp, + .mi = *mi, + .mi2 = *mi2, + }; + llist_add_tail(&p->entry, &cnlink->paging); + talloc_set_destructor(p, cnlink_paging_destructor); + + LOG_CNLINK(cnlink, DCN, LOGL_INFO, "Rx Paging from CN for %s %s\n", + osmo_mobile_identity_to_str_c(OTC_SELECT, mi), + osmo_mobile_identity_to_str_c(OTC_SELECT, mi2)); + return NULL; +} + +static const char *omi_from_ranap_ue_id(struct osmo_mobile_identity *mi, const RANAP_PermanentNAS_UE_ID_t *ranap_mi) +{ + if (!ranap_mi) + return "null UE ID"; + + if (ranap_mi->present != RANAP_PermanentNAS_UE_ID_PR_iMSI) + return talloc_asprintf(OTC_SELECT, "unsupported UE ID type %u in RANAP Paging", ranap_mi->present); - msg = ranap_new_msg_reset_ack(cnlink->pool->domain, NULL); + if (ranap_mi->choice.iMSI.size > sizeof(mi->imsi)) + return talloc_asprintf(OTC_SELECT, "invalid IMSI size %d > %zu", + ranap_mi->choice.iMSI.size, sizeof(mi->imsi)); - return osmo_sccp_tx_unitdata_msg(cnlink->hnbgw_sccp_user->sccp_user, - &cnlink->hnbgw_sccp_user->local_addr, - &cnlink->remote_addr, - msg); + *mi = (struct osmo_mobile_identity){ + .type = GSM_MI_TYPE_IMSI, + }; + ranap_bcd_decode(mi->imsi, sizeof(mi->imsi), ranap_mi->choice.iMSI.buf, ranap_mi->choice.iMSI.size); + LOGP(DCN, LOGL_DEBUG, "ranap MI %s = %s\n", osmo_hexdump(ranap_mi->choice.iMSI.buf, ranap_mi->choice.iMSI.size), + mi->imsi); + return NULL; } -/*********************************************************************** - * Incoming primitives from SCCP User SAP - ***********************************************************************/ +static const char *omi_from_ranap_temp_ue_id(struct osmo_mobile_identity *mi, const RANAP_TemporaryUE_ID_t *ranap_tmsi) +{ + const OCTET_STRING_t *tmsi_str; -static int cn_ranap_rx_reset_cmd(struct hnbgw_cnlink *cnlink, - const struct osmo_scu_unitdata_param *unitdata, - RANAP_InitiatingMessage_t *imsg) + if (!ranap_tmsi) + return "null UE ID"; + + switch (ranap_tmsi->present) { + case RANAP_TemporaryUE_ID_PR_tMSI: + tmsi_str = &ranap_tmsi->choice.tMSI; + break; + case RANAP_TemporaryUE_ID_PR_p_TMSI: + tmsi_str = &ranap_tmsi->choice.p_TMSI; + break; + default: + return talloc_asprintf(OTC_SELECT, "unsupported Temporary UE ID type %u in RANAP Paging", ranap_tmsi->present); + } + + *mi = (struct osmo_mobile_identity){ + .type = GSM_MI_TYPE_TMSI, + .tmsi = asn1str_to_u32(tmsi_str), + }; + LOGP(DCN, LOGL_DEBUG, "ranap temp UE ID = %s\n", osmo_mobile_identity_to_str_c(OTC_SELECT, mi)); + return NULL; +} + +static const char *cnlink_paging_add_ranap(struct hnbgw_cnlink *cnlink, RANAP_InitiatingMessage_t *imsg) { + RANAP_PagingIEs_t ies; + struct osmo_mobile_identity mi = {}; + struct osmo_mobile_identity mi2 = {}; RANAP_CN_DomainIndicator_t domain; - RANAP_ResetIEs_t ies; - int rc; + const char *errmsg; + + if (ranap_decode_pagingies(&ies, &imsg->value) < 0) + return "decoding RANAP IEs failed"; - rc = ranap_decode_reseties(&ies, &imsg->value); domain = ies.cN_DomainIndicator; - ranap_free_reseties(&ies); + errmsg = omi_from_ranap_ue_id(&mi, &ies.permanentNAS_UE_ID); - LOG_CNLINK(cnlink, DRANAP, LOGL_NOTICE, "Rx RESET from %s %s, returning ACK\n", - domain == DOMAIN_CS ? "IuCS" : "IuPS", - osmo_sccp_inst_addr_name(cnlink_sccp(cnlink), &unitdata->calling_addr)); - - /* FIXME: actually reset connections, if any */ - - if (transmit_reset_ack(cnlink)) - LOGP(DRANAP, LOGL_ERROR, "Error: cannot send RESET ACK to %s %s\n", - domain == DOMAIN_CS ? "IuCS" : "IuPS", - osmo_sccp_inst_addr_name(cnlink_sccp(cnlink), &unitdata->calling_addr)); + if (!errmsg && (ies.presenceMask & PAGINGIES_RANAP_TEMPORARYUE_ID_PRESENT)) + errmsg = omi_from_ranap_temp_ue_id(&mi2, &ies.temporaryUE_ID); - return rc; + ranap_free_pagingies(&ies); + LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "Decoded Paging: %s %s %s%s%s\n", + ranap_domain_name(domain), osmo_mobile_identity_to_str_c(OTC_SELECT, &mi), + mi2.type ? osmo_mobile_identity_to_str_c(OTC_SELECT, &mi2) : "-", + errmsg ? " -- MI error: " : "", + errmsg ? : ""); + + if (cnlink->pool->domain != domain) + return talloc_asprintf(OTC_SELECT, "message indicates domain %s, but this is %s on domain %s\n", + ranap_domain_name(domain), cnlink->name, ranap_domain_name(cnlink->pool->domain)); + + if (errmsg) + return errmsg; + + return cnlink_paging_add(cnlink, &mi, &mi2); +} + +/* If this cnlink has a recent Paging for the given MI, return true and drop the Paging record. + * Else return false. */ +static bool cnlink_match_paging_mi(struct hnbgw_cnlink *cnlink, const struct osmo_mobile_identity *mi, time_t timeout) +{ + struct cnlink_paging *p, *p2; + llist_for_each_entry_safe(p, p2, &cnlink->paging, entry) { + if (p->timestamp < timeout) { + talloc_free(p); + continue; + } + if (osmo_mobile_identity_cmp(&p->mi, mi) + && osmo_mobile_identity_cmp(&p->mi2, mi)) + continue; + talloc_free(p); + return true; + } + return false; } -static int cn_ranap_rx_reset_ack(struct hnbgw_cnlink *cnlink, - RANAP_SuccessfulOutcome_t *omsg) +static struct hnbgw_cnlink *cnlink_find_by_paging_mi(struct hnbgw_cnpool *cnpool, const struct osmo_mobile_identity *mi) { - RANAP_ResetAcknowledgeIEs_t ies; - int rc; - - rc = ranap_decode_resetacknowledgeies(&ies, &omsg->value); + struct hnbgw_cnlink *cnlink; + time_t timeout = 0; + const char *errmsg; - /* FUTURE: will do something useful in commit 'detect in/active CN links by RANAP RESET' - * Id3eefdea889a736fd5957b80280fa45b9547b792 */ + errmsg = cnlink_paging_gettime(NULL, &timeout); + if (errmsg) + LOGP(DCN, LOGL_ERROR, "%s\n", errmsg); - ranap_free_resetacknowledgeies(&ies); - return rc; + llist_for_each_entry(cnlink, &cnpool->cnlinks, entry) { + if (!cnlink_match_paging_mi(cnlink, mi, timeout)) + continue; + return cnlink; + } + return NULL; } static int cn_ranap_rx_paging_cmd(struct hnbgw_cnlink *cnlink, RANAP_InitiatingMessage_t *imsg, const uint8_t *data, unsigned int len) { + const char *errmsg; struct hnb_context *hnb; - RANAP_PagingIEs_t ies; - int rc; - rc = ranap_decode_pagingies(&ies, &imsg->value); - if (rc < 0) - return rc; + errmsg = cnlink_paging_add_ranap(cnlink, imsg); + if (errmsg) { + LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "Rx Paging from CN: %s. Dropping paging record." + " Later on, the Paging Response may be forwarded to the wrong CN peer.\n", + errmsg); + return -1; + } /* FIXME: determine which HNBs to send this Paging command, * rather than broadcasting to all HNBs */ llist_for_each_entry(hnb, &g_hnbgw->hnb_list, list) { - rc = rua_tx_udt(hnb, data, len); + rua_tx_udt(hnb, data, len); } - ranap_free_pagingies(&ies); return 0; } @@ -297,7 +447,7 @@ return -ENOENT; if (param->called_addr.ssn != OSMO_SCCP_SSN_RANAP) { - LOGP(DMAIN, LOGL_NOTICE, "N-UNITDATA.ind for unknown SSN %u\n", + LOGP(DCN, LOGL_NOTICE, "N-UNITDATA.ind for unknown SSN %u\n", param->called_addr.ssn); return -1; } @@ -315,7 +465,7 @@ if (!map || !map->cnlink) return -ENOENT; - LOGP(DMAIN, LOGL_DEBUG, "handle_cn_conn_conf() conn_id=%d, addrs: called=%s calling=%s responding=%s\n", + LOGP(DCN, LOGL_DEBUG, "handle_cn_conn_conf() conn_id=%d, addrs: called=%s calling=%s responding=%s\n", param->conn_id, cnlink_sccp_addr_to_str(map->cnlink, ¶m->called_addr), cnlink_sccp_addr_to_str(map->cnlink, ¶m->calling_addr), @@ -348,13 +498,129 @@ if (!map || !map->cnlink) return -ENOENT; - LOGP(DMAIN, LOGL_DEBUG, "handle_cn_disc_ind() conn_id=%u responding_addr=%s\n", + LOGP(DCN, LOGL_DEBUG, "handle_cn_disc_ind() conn_id=%u responding_addr=%s\n", param->conn_id, cnlink_sccp_addr_to_str(map->cnlink, ¶m->responding_addr)); return map_sccp_dispatch(map, MAP_SCCP_EV_RX_RELEASED, oph->msg); } +static struct hnbgw_cnlink *_cnlink_find_by_remote_pc(struct hnbgw_cnpool *cnpool, struct osmo_ss7_instance *cs7, uint32_t pc) +{ + struct hnbgw_cnlink *cnlink; + llist_for_each_entry(cnlink, &cnpool->cnlinks, entry) { + if (!cnlink->hnbgw_sccp_user) + continue; + if (cnlink->hnbgw_sccp_user->ss7 != cs7) + continue; + if ((cnlink->remote_addr.presence & OSMO_SCCP_ADDR_T_PC) == 0) + continue; + if (cnlink->remote_addr.pc != pc) + continue; + return cnlink; + } + return NULL; +} + +/* Find a cnlink by its remote sigtran point code on a given cs7 instance. */ +static struct hnbgw_cnlink *cnlink_find_by_remote_pc(struct osmo_ss7_instance *cs7, uint32_t pc) +{ + struct hnbgw_cnlink *cnlink; + cnlink = _cnlink_find_by_remote_pc(&g_hnbgw->sccp.cnpool_iucs, cs7, pc); + if (!cnlink) + cnlink = _cnlink_find_by_remote_pc(&g_hnbgw->sccp.cnpool_iups, cs7, pc); + return cnlink; +} + +static void handle_pcstate_ind(struct hnbgw_sccp_user *hsu, const struct osmo_scu_pcstate_param *pcst) +{ + struct hnbgw_cnlink *cnlink; + bool connected; + bool disconnected; + struct osmo_ss7_instance *cs7 = hsu->ss7; + + LOGP(DCN, LOGL_DEBUG, "N-PCSTATE ind: affected_pc=%u sp_status=%s remote_sccp_status=%s\n", + pcst->affected_pc, osmo_sccp_sp_status_name(pcst->sp_status), + osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status)); + + /* If we don't care about that point-code, ignore PCSTATE. */ + cnlink = cnlink_find_by_remote_pc(cs7, pcst->affected_pc); + if (!cnlink) + return; + + /* See if this marks the point code to have become available, or to have been lost. + * + * I want to detect two events: + * - connection event (both indicators say PC is reachable). + * - disconnection event (at least one indicator says the PC is not reachable). + * + * There are two separate incoming indicators with various possible values -- the incoming events can be: + * + * - neither connection nor disconnection indicated -- just indicating congestion + * connected == false, disconnected == false --> do nothing. + * - both incoming values indicate that we are connected + * --> trigger connected + * - both indicate we are disconnected + * --> trigger disconnected + * - one value indicates 'connected', the other indicates 'disconnected' + * --> trigger disconnected + * + * Congestion could imply that we're connected, but it does not indicate that a PC's reachability changed, so no need to + * trigger on that. + */ + connected = false; + disconnected = false; + + switch (pcst->sp_status) { + case OSMO_SCCP_SP_S_ACCESSIBLE: + connected = true; + break; + case OSMO_SCCP_SP_S_INACCESSIBLE: + disconnected = true; + break; + default: + case OSMO_SCCP_SP_S_CONGESTED: + /* Neither connecting nor disconnecting */ + break; + } + + switch (pcst->remote_sccp_status) { + case OSMO_SCCP_REM_SCCP_S_AVAILABLE: + if (!disconnected) + connected = true; + break; + case OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN: + case OSMO_SCCP_REM_SCCP_S_UNEQUIPPED: + case OSMO_SCCP_REM_SCCP_S_INACCESSIBLE: + disconnected = true; + connected = false; + break; + default: + case OSMO_SCCP_REM_SCCP_S_CONGESTED: + /* Neither connecting nor disconnecting */ + break; + } + + if (disconnected && cnlink_is_conn_ready(cnlink)) { + LOG_CNLINK(cnlink, DCN, LOGL_NOTICE, + "now unreachable: N-PCSTATE ind: pc=%u sp_status=%s remote_sccp_status=%s\n", + pcst->affected_pc, + osmo_sccp_sp_status_name(pcst->sp_status), + osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status)); + /* A previously usable cnlink has disconnected. Kick it back to DISC state. */ + cnlink_set_disconnected(cnlink); + } else if (connected && !cnlink_is_conn_ready(cnlink)) { + LOG_CNLINK(cnlink, DCN, LOGL_NOTICE, + "now available: N-PCSTATE ind: pc=%u sp_status=%s remote_sccp_status=%s\n", + pcst->affected_pc, + osmo_sccp_sp_status_name(pcst->sp_status), + osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status)); + /* A previously unusable cnlink has become reachable. Trigger immediate RANAP RESET -- we would resend a + * RESET either way, but we might as well do it now to speed up connecting. */ + cnlink_resend_reset(cnlink); + } +} + /* Entry point for primitives coming up from SCCP User SAP */ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *ctx) { @@ -363,10 +629,10 @@ struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph; int rc = 0; - LOGP(DMAIN, LOGL_DEBUG, "sccp_sap_up(%s)\n", osmo_scu_prim_name(oph)); + LOGP(DCN, LOGL_DEBUG, "sccp_sap_up(%s)\n", osmo_scu_prim_name(oph)); if (!scu) { - LOGP(DMAIN, LOGL_ERROR, + LOGP(DCN, LOGL_ERROR, "sccp_sap_up(): NULL osmo_sccp_user, cannot send prim (sap %u prim %u op %d)\n", oph->sap, oph->primitive, oph->operation); return -1; @@ -374,7 +640,7 @@ hsu = osmo_sccp_user_get_priv(scu); if (!hsu) { - LOGP(DMAIN, LOGL_ERROR, + LOGP(DCN, LOGL_ERROR, "sccp_sap_up(): NULL hnbgw_sccp_user, cannot send prim (sap %u prim %u op %d)\n", oph->sap, oph->primitive, oph->operation); return -1; @@ -396,11 +662,11 @@ rc = handle_cn_disc_ind(hsu, &prim->u.disconnect, oph); break; case OSMO_PRIM(OSMO_SCU_PRIM_N_PCSTATE, PRIM_OP_INDICATION): - LOGP(DMAIN, LOGL_DEBUG, "Ignoring prim %s from SCCP USER SAP\n", - osmo_scu_prim_hdr_name_c(OTC_SELECT, oph)); + handle_pcstate_ind(hsu, &prim->u.pcstate); break; + default: - LOGP(DMAIN, LOGL_ERROR, + LOGP(DCN, LOGL_ERROR, "Received unknown prim %u from SCCP USER SAP\n", OSMO_PRIM_HDR(oph)); break; @@ -425,7 +691,7 @@ if (!addr_name) { osmo_sccp_make_addr_pc_ssn(dest, default_pc, OSMO_SCCP_SSN_RANAP); if (label) - LOGP(DMAIN, LOGL_INFO, "%s remote addr not configured, using default: %s\n", label, + LOGP(DCN, LOGL_INFO, "%s remote addr not configured, using default: %s\n", label, osmo_sccp_addr_name(*ss7, dest)); return 0; } @@ -433,7 +699,7 @@ *ss7 = osmo_sccp_addr_by_name(dest, addr_name); if (!*ss7) { if (label) - LOGP(DMAIN, LOGL_ERROR, "%s remote addr: no such SCCP address book entry: '%s'\n", + LOGP(DCN, LOGL_ERROR, "%s remote addr: no such SCCP address book entry: '%s'\n", label, addr_name); return -1; } @@ -442,14 +708,11 @@ if (!addr_has_pc_and_ssn(dest)) { if (label) - LOGP(DMAIN, LOGL_ERROR, "Invalid/incomplete %s remote-addr: %s\n", + LOGP(DCN, LOGL_ERROR, "Invalid/incomplete %s remote-addr: %s\n", label, osmo_sccp_addr_name(*ss7, dest)); return -1; } - if (label) - LOGP(DRANAP, LOGL_NOTICE, "Remote %s SCCP addr: %s\n", - label, osmo_sccp_addr_name(*ss7, dest)); return 0; } @@ -511,6 +774,17 @@ cnlink->hnbgw_sccp_user = NULL; } +static void hnbgw_cnlink_log_self(struct hnbgw_cnlink *cnlink) +{ + struct osmo_ss7_instance *ss7 = cnlink->hnbgw_sccp_user->ss7; + LOG_CNLINK(cnlink, DCN, LOGL_NOTICE, "using: cs7-%u %s <-> %s %s %s\n", + ss7->cfg.id, + /* printing the entire SCCP address is quite long, rather just print the point-code */ + osmo_ss7_pointcode_print(ss7, cnlink->hnbgw_sccp_user->local_addr.pc), + osmo_ss7_pointcode_print2(ss7, cnlink->remote_addr.pc), + cnlink->name, cnlink->use.remote_addr_name ? : "(default remote point-code)"); +} + /* If not present yet, set up all of osmo_ss7_instance, osmo_sccp_instance and hnbgw_sccp_user for the given cnlink. * The cs7 instance nr to use is determined by cnlink->remote_addr_name, or cs7 instance 0 if that is not present. * Set cnlink->hnbgw_sccp_user to the new SCCP instance. Return 0 on success, negative on error. */ @@ -569,8 +843,9 @@ if (hsu->ss7 != ss7) continue; cnlink->hnbgw_sccp_user = hsu; - LOG_CNLINK(cnlink, DCN, LOGL_NOTICE, "using existing SCCP instance %s on cs7 instance %u\n", + LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "using existing SCCP instance %s on cs7 instance %u\n", hsu->name, ss7->cfg.id); + hnbgw_cnlink_log_self(cnlink); return 0; } /* else cnlink->hnbgw_sccp_user stays NULL and is set up below. */ @@ -604,7 +879,7 @@ sccp_user = osmo_sccp_user_bind_pc(sccp, "OsmoHNBGW", sccp_sap_up, OSMO_SCCP_SSN_RANAP, local_pc); if (!sccp_user) { - LOGP(DMAIN, LOGL_ERROR, "Failed to init SCCP User\n"); + LOGP(DCN, LOGL_ERROR, "Failed to init SCCP User\n"); return -1; } @@ -621,6 +896,8 @@ llist_add_tail(&hsu->entry, &g_hnbgw->sccp.users); cnlink->hnbgw_sccp_user = hsu; + + hnbgw_cnlink_log_self(cnlink); return 0; } @@ -642,26 +919,6 @@ hnbgw_cnpool_cnlinks_start_or_restart(cnpool); } -static struct hnbgw_cnlink *cnlink_alloc(struct hnbgw_cnpool *cnpool, int nr) -{ - struct hnbgw_cnlink *cnlink; - cnlink = talloc_zero(g_hnbgw, struct hnbgw_cnlink); - *cnlink = (struct hnbgw_cnlink){ - .name = talloc_asprintf(cnlink, "%s-%d", cnpool->peer_name, nr), - .pool = cnpool, - .nr = nr, - .vty = { - /* VTY config defaults for the new cnlink */ - .nri_ranges = osmo_nri_ranges_alloc(cnlink), - }, - .allow_attach = true, - }; - INIT_LLIST_HEAD(&cnlink->map_list); - - llist_add_tail(&cnlink->entry, &cnpool->cnlinks); - return cnlink; -} - struct hnbgw_cnlink *cnlink_get_nr(struct hnbgw_cnpool *cnpool, int nr, bool create_if_missing) { struct hnbgw_cnlink *cnlink; @@ -700,21 +957,167 @@ return NULL; } +static bool is_cnlink_usable(struct hnbgw_cnlink *cnlink, bool is_emerg) +{ + if (is_emerg && !cnlink->allow_emerg) + return false; + if (!cnlink->hnbgw_sccp_user || !cnlink->hnbgw_sccp_user->sccp_user) + return false; + if (!cnlink_is_conn_ready(cnlink)) + return false; + return true; +} + +/* Decide which MSC/SGSN to forward this Complete Layer 3 request to. The current Layer 3 Info is passed in map->l3. + * a) If the subscriber was previously paged from a particular CN link, that CN link shall receive the Paging Response. + * b) If the message contains an NRI indicating a particular CN link that is currently connected, that CN link shall + * handle this conn. + * c) All other cases distribute the messages across connected CN links in a round-robin fashion. + */ struct hnbgw_cnlink *hnbgw_cnlink_select(struct hnbgw_context_map *map) { struct hnbgw_cnpool *cnpool = map->is_ps ? &g_hnbgw->sccp.cnpool_iups : &g_hnbgw->sccp.cnpool_iucs; struct hnbgw_cnlink *cnlink; - /* FUTURE: soon we will pick one of many configurable CN peers from a pool. There will be more input arguments - * (MI, or TMSI, or NRI decoded from RANAP) and this function will do round robin for new subscribers. */ + struct hnbgw_cnlink *round_robin_next = NULL; + struct hnbgw_cnlink *round_robin_first = NULL; + unsigned int round_robin_next_nr; + int16_t nri_v = -1; + bool is_null_nri = false; + uint8_t nri_bitlen = cnpool->use.nri_bitlen; + + /* Match IMSI with previous Paging */ + if (map->l3.gsm48_msg_type == GSM48_MT_RR_PAG_RESP) { + cnlink = cnlink_find_by_paging_mi(cnpool, &map->l3.mi); + if (cnlink) { + LOG_MAP(map, DCN, LOGL_INFO, "CN link paging record selects %s %d\n", cnpool->peer_name, + cnlink->nr); + rate_ctr_inc(rate_ctr_group_get_ctr(cnlink->ctrs, CNLINK_CTR_CNPOOL_SUBSCR_PAGED)); + return cnlink; + } + /* If there is no match, go on with other ways */ + } + +#define LOG_NRI(LOGLEVEL, FORMAT, ARGS...) \ + LOG_MAP(map, DCN, LOGLEVEL, "%s NRI(%dbit)=0x%x=%d: " FORMAT, osmo_mobile_identity_to_str_c(OTC_SELECT, &map->l3.mi), \ + nri_bitlen, nri_v, nri_v, ##ARGS) + + /* Get the NRI bits either from map->l3.nri, or extract NRI bits from TMSI. + * The NRI possibly indicates which MSC is responsible. */ + if (map->l3.gmm_nri_container >= 0) { + nri_v = map->l3.gmm_nri_container; + /* The 'TMSI based NRI container' is always 10 bits long. If the relevant NRI length is configured to be + * less than that, ignore the lower bits. */ + if (nri_bitlen < 10) + nri_v >>= 10 - nri_bitlen; + } else if (map->l3.mi.type == GSM_MI_TYPE_TMSI) { + if (osmo_tmsi_nri_v_get(&nri_v, map->l3.mi.tmsi, nri_bitlen)) { + LOG_NRI(LOGL_ERROR, "Unable to retrieve NRI from TMSI 0x%x, nri_bitlen == %u\n", map->l3.mi.tmsi, + nri_bitlen); + nri_v = -1; + } + } + + if (map->l3.from_other_plmn && nri_v >= 0) { + /* If a subscriber was previously attached to a different PLMN, it might still send the other + * PLMN's TMSI identity in an IMSI Attach. The LU sends a LAI indicating the previous PLMN. If + * it mismatches our PLMN, ignore the NRI. */ + LOG_NRI(LOGL_DEBUG, + "This Complete Layer 3 message indicates a switch from another PLMN. Ignoring the NRI.\n"); + nri_v = -1; + } + + if (nri_v >= 0) + is_null_nri = osmo_nri_v_matches_ranges(nri_v, cnpool->use.null_nri_ranges); + if (is_null_nri) + LOG_NRI(LOGL_DEBUG, "this is a NULL-NRI\n"); + + /* Iterate CN links to find one that matches the extracted NRI, and the next round-robin target for the case no + * NRI match is found. */ + round_robin_next_nr = (map->l3.is_emerg ? cnpool->round_robin_next_emerg_nr : cnpool->round_robin_next_nr); llist_for_each_entry(cnlink, &cnpool->cnlinks, entry) { - if (!cnlink->hnbgw_sccp_user || !cnlink->hnbgw_sccp_user->sccp_user) + bool nri_matches_cnlink = (nri_v >= 0 && osmo_nri_v_matches_ranges(nri_v, cnlink->use.nri_ranges)); + + if (!is_cnlink_usable(cnlink, map->l3.is_emerg)) { + if (nri_matches_cnlink) { + LOG_NRI(LOGL_DEBUG, "NRI matches %s %d, but this %s is currently not connected\n", + cnpool->peer_name, cnlink->nr, cnpool->peer_name); + rate_ctr_inc(rate_ctr_group_get_ctr(cnlink->ctrs, CNLINK_CTR_CNPOOL_SUBSCR_ATTACH_LOST)); + } continue; - LOG_MAP(map, DCN, LOGL_INFO, "Selected %s / %s\n", - cnlink->name, - cnlink->hnbgw_sccp_user->name); - return cnlink; + } + + /* Return CN link if it matches this NRI, with some debug logging. */ + if (nri_matches_cnlink) { + if (is_null_nri) { + LOG_NRI(LOGL_DEBUG, "NRI matches %s %d, but this NRI is also configured as NULL-NRI\n", + cnpool->peer_name, cnlink->nr); + } else { + LOG_NRI(LOGL_INFO, "NRI match selects %s %d\n", cnpool->peer_name, cnlink->nr); + rate_ctr_inc(rate_ctr_group_get_ctr(cnlink->ctrs, CNLINK_CTR_CNPOOL_SUBSCR_KNOWN)); + if (map->l3.is_emerg) { + rate_ctr_inc(rate_ctr_group_get_ctr(cnlink->ctrs, CNLINK_CTR_CNPOOL_EMERG_FORWARDED)); + rate_ctr_inc(rate_ctr_group_get_ctr(cnpool->ctrs, CNPOOL_CTR_EMERG_FORWARDED)); + } + return cnlink; + } + } + + /* Figure out the next round-robin MSC. The MSCs may appear unsorted in net->mscs. Make sure to linearly + * round robin the MSCs by number: pick the lowest msc->nr >= round_robin_next_nr, and also remember the + * lowest available msc->nr to wrap back to that in case no next MSC is left. + * + * MSCs configured with `no allow-attach` do not accept new subscribers and hence must not be picked by + * round-robin. Such an MSC still provides service for already attached subscribers: those that + * successfully performed IMSI-Attach and have a TMSI with an NRI pointing at that MSC. We only avoid + * adding IMSI-Attach of new subscribers. The idea is that the MSC is in a mode of off-loading + * subscribers, and the MSC decides when each subscriber is off-loaded, by assigning the NULL-NRI in a + * new TMSI (at the next periodical LU). So until the MSC decides to offload, an attached subscriber + * remains attached to that MSC and is free to use its services. + */ + if (!cnlink->allow_attach) + continue; + /* Find the allowed cnlink with the lowest nr */ + if (!round_robin_first || cnlink->nr < round_robin_first->nr) + round_robin_first = cnlink; + /* Find the allowed cnlink with the lowest nr >= round_robin_next_nr */ + if (cnlink->nr >= round_robin_next_nr + && (!round_robin_next || cnlink->nr < round_robin_next->nr)) + round_robin_next = cnlink; + } + + if (nri_v >= 0 && !is_null_nri) + LOG_NRI(LOGL_DEBUG, "No %s found for this NRI, doing round-robin\n", cnpool->peer_name); + + /* No dedicated CN link found. Choose by round-robin. + * If round_robin_next is NULL, there are either no more CN links at/after round_robin_next_nr, or none of + * them are usable -- wrap to the start. */ + cnlink = round_robin_next ? : round_robin_first; + if (!cnlink) { + rate_ctr_inc(rate_ctr_group_get_ctr(cnpool->ctrs, CNPOOL_CTR_SUBSCR_NO_CNLINK)); + if (map->l3.is_emerg) + rate_ctr_inc(rate_ctr_group_get_ctr(cnpool->ctrs, CNPOOL_CTR_EMERG_LOST)); + return NULL; } - return NULL; + + LOG_MAP(map, DCN, LOGL_INFO, "CN link round-robin selects %s %d\n", cnpool->peer_name, cnlink->nr); + + if (is_null_nri) + rate_ctr_inc(rate_ctr_group_get_ctr(cnlink->ctrs, CNLINK_CTR_CNPOOL_SUBSCR_REATTACH)); + else + rate_ctr_inc(rate_ctr_group_get_ctr(cnlink->ctrs, CNLINK_CTR_CNPOOL_SUBSCR_NEW)); + + if (map->l3.is_emerg) { + rate_ctr_inc(rate_ctr_group_get_ctr(cnlink->ctrs, CNLINK_CTR_CNPOOL_EMERG_FORWARDED)); + rate_ctr_inc(rate_ctr_group_get_ctr(cnpool->ctrs, CNPOOL_CTR_EMERG_FORWARDED)); + } + + /* A CN link was picked by round-robin, so update the next round-robin nr to pick */ + if (map->l3.is_emerg) + cnpool->round_robin_next_emerg_nr = cnlink->nr + 1; + else + cnpool->round_robin_next_nr = cnlink->nr + 1; + return cnlink; +#undef LOG_NRI } char *cnlink_sccp_addr_to_str(struct hnbgw_cnlink *cnlink, const struct osmo_sccp_addr *addr) @@ -724,3 +1127,81 @@ return osmo_sccp_addr_dump(addr); return osmo_sccp_inst_addr_to_str_c(OTC_SELECT, sccp, addr); } + +static const struct rate_ctr_desc cnlink_ctr_description = { + + /* Indicators for CN pool usage */ + CNLINK_CTR_CNPOOL_SUBSCR_NEW = { + "cnpool:subscr:new", + "Complete Layer 3 requests assigned to this CN link by round-robin (no NRI was assigned yet).", + }, + CNLINK_CTR_CNPOOL_SUBSCR_REATTACH = { + "cnpool:subscr:reattach", + "Complete Layer 3 requests assigned to this CN link by round-robin because the subscriber indicates a" + " NULL-NRI (previously assigned by another CN link).", + }, + CNLINK_CTR_CNPOOL_SUBSCR_KNOWN = { + "cnpool:subscr:known", + "Complete Layer 3 requests directed to this CN link because the subscriber indicates an NRI of this CN link.", + }, + CNLINK_CTR_CNPOOL_SUBSCR_PAGED = { + "cnpool:subscr:paged", + "Paging Response directed to this CN link because the subscriber was recently paged by this CN link.", + }, + CNLINK_CTR_CNPOOL_SUBSCR_ATTACH_LOST = { + "cnpool:subscr:attach_lost", + "A subscriber indicates an NRI value matching this CN link, but the CN link is not connected:" + " a re-attach to another CN link (if available) was forced, with possible service failure.", + }, + CNLINK_CTR_CNPOOL_EMERG_FORWARDED = { + "cnpool:emerg:forwarded", + "Emergency call requests forwarded to this CN link.", + }, +}; + +const struct rate_ctr_group_desc msc_ctrg_desc = { + "msc", + "MSC", + OSMO_STATS_CLASS_GLOBAL, + ARRAY_SIZE(cnlink_ctr_description), + cnlink_ctr_description, +}; + +const struct rate_ctr_group_desc sgsn_ctrg_desc = { + "sgsn", + "SGSN", + OSMO_STATS_CLASS_GLOBAL, + ARRAY_SIZE(cnlink_ctr_description), + cnlink_ctr_description, +}; + +static const struct rate_ctr_desc cnpool_ctr_description = { + CNPOOL_CTR_SUBSCR_NO_CNLINK = { + "cnpool:subscr:no_cnlink", + "Complete Layer 3 requests lost because no connected CN link is found available", + }, + CNPOOL_CTR_EMERG_FORWARDED = { + "cnpool:emerg:forwarded", + "Emergency call requests forwarded to a CN link (see also per-CN-link counters)", + }, + CNPOOL_CTR_EMERG_LOST = { + "cnpool:emerg:lost", + "Emergency call requests lost because no CN link was found available", + }, +}; + +const struct rate_ctr_group_desc iucs_ctrg_desc = { + "iucs", + "IuCS", + OSMO_STATS_CLASS_GLOBAL, + ARRAY_SIZE(cnpool_ctr_description), + cnpool_ctr_description, +}; + +const struct rate_ctr_group_desc iups_ctrg_desc = { + "iups", + "IuPS", + OSMO_STATS_CLASS_GLOBAL, + ARRAY_SIZE(cnpool_ctr_description), + cnpool_ctr_description, +};
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/src/osmo-hnbgw/hnbgw_hnbap.c -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/src/osmo-hnbgw/hnbgw_hnbap.c
Changed
@@ -119,7 +119,7 @@ ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_HNBAP_HNBRegisterAccept, &accept_out); - LOGHNB(ctx, DHNBAP, LOGL_NOTICE, "Accepting HNB-REGISTER-REQ from %s\n", ctx->identity_info); + LOGHNB(ctx, DHNBAP, LOGL_NOTICE, "Accepting HNB-REGISTER-REQ\n"); return hnbgw_hnbap_tx(ctx, msg); } @@ -409,14 +409,10 @@ int rc; struct osmo_plmn_id plmn; struct osmo_fd *ofd = osmo_stream_srv_get_ofd(ctx->conn); - char nameOSMO_SOCK_NAME_MAXLEN; - - osmo_sock_get_name_buf(name, sizeof(name), ofd->fd); rc = hnbap_decode_hnbregisterrequesties(&ies, in); if (rc < 0) { - LOGHNB(ctx, DHNBAP, LOGL_ERROR, "Failure to decode HNB-REGISTER-REQ %s from %s: rc=%d\n", - ctx->identity_info, name, rc); + LOGHNB(ctx, DHNBAP, LOGL_ERROR, "Failure to decode HNB-REGISTER-REQ: rc=%d\n", rc); return rc; } @@ -460,17 +456,15 @@ /* If new conn registering same HNB is from anoter remote addr+port, let's reject it to avoid * misconfigurations or someone trying to impersonate an already working HNB: */ - LOGHNB(ctx, DHNBAP, LOGL_ERROR, "rejecting HNB-REGISTER-REQ with duplicate cell identity " - "MCC=%u,MNC=%u,LAC=%u,RAC=%u,SAC=%u,CID=%u from %s\n", - ctx->id.mcc, ctx->id.mnc, ctx->id.lac, ctx->id.rac, ctx->id.sac, ctx->id.cid, name); + LOGHNB(ctx, DHNBAP, LOGL_ERROR, "rejecting HNB-REGISTER-REQ with duplicate cell identity %s\n", + umts_cell_id_name(&ctx->id)); hnbap_free_hnbregisterrequesties(&ies); return hnbgw_tx_hnb_register_rej(ctx); } } - LOGHNB(ctx, DHNBAP, LOGL_DEBUG, "HNB-REGISTER-REQ %s MCC=%u,MNC=%u,LAC=%u,RAC=%u,SAC=%u,CID=%u from %s%s\n", - ctx->identity_info, ctx->id.mcc, ctx->id.mnc, ctx->id.lac, ctx->id.rac, ctx->id.sac, ctx->id.cid, - name, ctx->hnb_registered ? " (re-connecting)" : ""); + LOGHNB(ctx, DHNBAP, LOGL_DEBUG, "HNB-REGISTER-REQ %s %s%s\n", + ctx->identity_info, umts_cell_id_name(&ctx->id), ctx->hnb_registered ? " (re-connecting)" : ""); /* The HNB is already registered, and we are seeing a new HNB Register Request. The HNB has restarted * without us noticing. Clearly, the HNB does not expect any UE state to be active here, so discard any
View file
osmo-hnbgw_1.4.0.105.3a084.tar.xz/src/osmo-hnbgw/hnbgw_l3.c
Added
@@ -0,0 +1,317 @@ +/* OsmoHNBGW implementation of CS and PS Level3 message decoding (NAS PDU) */ + +/* Copyright 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * + * Author: Neels Janosch Hofmeyr <nhofmeyr@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 "config.h" + +#include "asn1helpers.h" + +#include <osmocom/gsm/gsm48.h> +#include <osmocom/gsm/protocol/gsm_04_08_gprs.h> + +#include <osmocom/hnbgw/hnbgw.h> +#include <osmocom/hnbgw/hnbgw_rua.h> +#include <osmocom/hnbgw/context_map.h> +#include <osmocom/ranap/ranap_ies_defs.h> + +static const struct tlv_definition gsm48_gmm_att_tlvdef = { + .def = { + GSM48_IE_GMM_CIPH_CKSN = { TLV_TYPE_FIXED, 1 }, + GSM48_IE_GMM_TIMER_READY = { TLV_TYPE_TV, 1 }, + GSM48_IE_GMM_TMSI_BASED_NRI_C = { TLV_TYPE_TLV }, + GSM48_IE_GMM_ALLOC_PTMSI = { TLV_TYPE_TLV, 0 }, + GSM48_IE_GMM_PTMSI_SIG = { TLV_TYPE_FIXED, 3 }, + GSM48_IE_GMM_AUTH_RAND = { TLV_TYPE_FIXED, 16 }, + GSM48_IE_GMM_AUTH_SRES = { TLV_TYPE_FIXED, 4 }, + GSM48_IE_GMM_IMEISV = { TLV_TYPE_TLV, 0 }, + GSM48_IE_GMM_DRX_PARAM = { TLV_TYPE_FIXED, 2 }, + GSM48_IE_GMM_MS_NET_CAPA = { TLV_TYPE_TLV, 0 }, + GSM48_IE_GMM_PDP_CTX_STATUS = { TLV_TYPE_TLV, 0 }, + GSM48_IE_GMM_PS_LCS_CAPA = { TLV_TYPE_TLV, 0 }, + GSM48_IE_GMM_GMM_MBMS_CTX_ST = { TLV_TYPE_TLV, 0 }, + }, +}; + +static void decode_gmm_tlv(struct osmo_mobile_identity *mi, + struct osmo_routing_area_id *old_ra, + int *nri, + const uint8_t *tlv_data, size_t tlv_len, bool allow_hex) +{ + struct tlv_parsed tp; + struct tlv_p_entry *e; + + tlv_parse(&tp, &gsm48_gmm_att_tlvdef, tlv_data, tlv_len, 0, 0); + + e = TLVP_GET(&tp, GSM48_IE_GMM_TMSI_BASED_NRI_C); + if (e) { + *nri = e->val0; + *nri <<= 2; + *nri |= e->val1 >> 6; + } +} + +static int mobile_identity_decode_from_gmm_att_req(struct osmo_mobile_identity *mi, + struct osmo_routing_area_id *old_ra, + int *nri, + const uint8_t *l3_data, size_t l3_len, bool allow_hex) +{ + const struct gsm48_hdr *gh = (void *)l3_data; + const uint8_t *cur = gh->data; + const uint8_t *end = l3_data + l3_len; + const uint8_t *mi_data; + uint8_t mi_len; + uint8_t msnc_len; + uint8_t ms_ra_acc_cap_len; + int rc; + + /* MS network capability 10.5.5.12 */ + msnc_len = *cur++; + cur += msnc_len; + + /* aTTACH Type 10.5.5.2 */ + cur++; + + /* DRX parameter 10.5.5.6 */ + cur += 2; + + /* Mobile Identity (P-TMSI or IMSI) 10.5.1.4 */ + mi_len = *cur++; + mi_data = cur; + cur += mi_len; + + if (cur >= end) + return -ENOSPC; + + rc = osmo_mobile_identity_decode(mi, mi_data, mi_len, allow_hex); + if (rc) + return rc; + + /* Old routing area identification 10.5.5.15. */ + rc = osmo_routing_area_id_decode(old_ra, cur, end - cur); + if (rc < 0) + return rc; + cur += rc; + + /* MS Radio Access Capability 10.5.5.12a */ + ms_ra_acc_cap_len = *cur++; + cur += ms_ra_acc_cap_len; + + if (cur > end) + return -ENOSPC; + + decode_gmm_tlv(mi, old_ra, nri, cur, end - cur, allow_hex); + return 0; +} + +static int mobile_identity_decode_from_gmm_rau_req(struct osmo_mobile_identity *mi, + struct osmo_routing_area_id *old_ra, + int *nri, + const uint8_t *l3_data, size_t l3_len, bool allow_hex) +{ + const struct gsm48_hdr *gh = (void *)l3_data; + const uint8_t *cur = gh->data; + const uint8_t *end = l3_data + l3_len; + uint8_t ms_ra_acc_cap_len; + int rc; + + /* Update Type 10.5.5.18 */ + cur++; + if (cur >= end) + return -ENOSPC; + + /* Old routing area identification 10.5.5.15 */ + rc = osmo_routing_area_id_decode(old_ra, cur, end - cur); + if (rc < 0) + return rc; + cur += rc; + if (cur >= end) + return -ENOSPC; + + /* MS Radio Access Capability 10.5.5.12a */ + ms_ra_acc_cap_len = *cur++; + cur += ms_ra_acc_cap_len; + + if (cur > end) + return -ENOSPC; + + decode_gmm_tlv(mi, old_ra, nri, cur, end - cur, allow_hex); + return 0; +} + +static int peek_l3_nas(struct hnbgw_context_map *map, const uint8_t *nas_pdu, size_t len, + const struct osmo_plmn_id *local_plmn) +{ + const struct gsm48_hdr *gh; + int8_t pdisc; + uint8_t mtype; + const struct gsm48_loc_upd_req *lu; + struct gsm48_service_request *cm; + struct osmo_location_area_id old_lai; + struct osmo_routing_area_id old_ra = {}; + int nri = -1; + + map->l3 = (struct hnbgw_l3_peek){ + .gmm_nri_container = -1, + }; + + /* Get the mobile identity from CS MM -- the PS GMM happens further down. + * This will return an error for GMM messages, ignore that. */ + if (!map->is_ps) + osmo_mobile_identity_decode_from_l3_buf(&map->l3.mi, nas_pdu, len, false); + + /* Get is_emerg and from_other_plmn */ + if (len < sizeof(*gh)) { + LOGP(DCN, LOGL_ERROR, "Layer 3 message too short for header\n"); + return -EINVAL; + } + + gh = (void *)nas_pdu; + pdisc = gsm48_hdr_pdisc(gh); + mtype = gsm48_hdr_msg_type(gh); + + map->l3.gsm48_pdisc = pdisc; + map->l3.gsm48_msg_type = mtype; + + /* Determine from_other_plmn and is_emerg */ + switch (pdisc) { + case GSM48_PDISC_MM: + + switch (mtype) { + case GSM48_MT_MM_LOC_UPD_REQUEST: + if (len < sizeof(*gh) + sizeof(*lu)) { + LOGP(DCN, LOGL_ERROR, "LU Req message too short\n"); + break; + } + + lu = (struct gsm48_loc_upd_req *)gh->data; + gsm48_decode_lai2(&lu->lai, &old_lai); + + map->l3.from_other_plmn = (osmo_plmn_cmp(&old_lai.plmn, local_plmn) != 0); + if (map->l3.from_other_plmn) + LOGP(DRUA, LOGL_INFO, "LU from other PLMN: old LAI=%s my PLMN=%s\n", + osmo_plmn_name_c(OTC_SELECT, &old_lai.plmn), + osmo_plmn_name_c(OTC_SELECT, local_plmn)); + break; + + case GSM48_MT_MM_CM_SERV_REQ: + if (len < sizeof(*gh) + sizeof(*cm)) { + LOGP(DRUA, LOGL_ERROR, "CM Service Req message too short\n"); + break; + } + cm = (struct gsm48_service_request *)&gh->data0; + map->l3.is_emerg = (cm->cm_service_type == GSM48_CMSERV_EMERGENCY); + LOGP(DRUA, LOGL_DEBUG, "CM Service is_emerg=%d\n", map->l3.is_emerg); + break; + + default: + break; + } + break; + + case GSM48_PDISC_MM_GPRS: + switch (mtype) { + case GSM48_MT_GMM_ATTACH_REQ: + mobile_identity_decode_from_gmm_att_req(&map->l3.mi, &old_ra, &nri, nas_pdu, len, false); + + LOGP(DRUA, LOGL_DEBUG, "GMM Attach Req mi=%s old_ra=%s nri:%d=0x%x\n", + osmo_mobile_identity_to_str_c(OTC_SELECT, &map->l3.mi), + osmo_rai_name2_c(OTC_SELECT, &old_ra), + nri, nri); + + if (old_ra.lac.plmn.mcc && osmo_plmn_cmp(&old_ra.lac.plmn, local_plmn)) { + map->l3.from_other_plmn = true; + LOGP(DRUA, LOGL_INFO, "GMM Attach Req from other PLMN: old RAI=%s my PLMN=%s\n", + osmo_rai_name2_c(OTC_SELECT, &old_ra), + osmo_plmn_name_c(OTC_SELECT, local_plmn)); + } + + if (nri >= 0) + map->l3.gmm_nri_container = nri; + + break; + + case GSM48_MT_GMM_RA_UPD_REQ: + mobile_identity_decode_from_gmm_rau_req(&map->l3.mi, &old_ra, &nri, nas_pdu, len, false); + + LOGP(DRUA, LOGL_DEBUG, "GMM Routing Area Upd Req mi=%s old_ra=%s nri:%d=0x%x\n", + osmo_mobile_identity_to_str_c(OTC_SELECT, &map->l3.mi), + osmo_rai_name2_c(OTC_SELECT, &old_ra), + nri, nri); + + if (old_ra.lac.plmn.mcc && osmo_plmn_cmp(&old_ra.lac.plmn, local_plmn)) { + map->l3.from_other_plmn = true; + LOGP(DRUA, LOGL_INFO, "GMM Routing Area Upd Req from other PLMN: old RAI=%s my PLMN=%s\n", + osmo_rai_name2_c(OTC_SELECT, &old_ra), + osmo_plmn_name_c(OTC_SELECT, local_plmn)); + } + + if (nri >= 0) + map->l3.gmm_nri_container = nri; + + break; + } + break; + + default: + break; + } + + return 0; +} + +static int peek_l3_initial_ue(struct hnbgw_context_map *map, const RANAP_InitialUE_MessageIEs_t *ies) +{ + struct osmo_plmn_id local_plmn; + + if (g_hnbgw->config.plmn.mcc) { + /* The user has configured a PLMN */ + local_plmn = g_hnbgw->config.plmn; + } else { + /* The user has not configured a PLMN, guess from the InitialUE message's LAI IE's PLMN */ + if (ies->lai.pLMNidentity.size < 3) { + LOGP(DCN, LOGL_ERROR, "Missing PLMN in RANAP InitialUE message\n"); + return -EINVAL; + } + osmo_plmn_from_bcd(ies->lai.pLMNidentity.buf, &local_plmn); + } + + return peek_l3_nas(map, ies->nas_pdu.buf, ies->nas_pdu.size, &local_plmn); +} + +/* Extract a Layer 3 message (NAS PDU) from the RANAP message, and put the info obtained in map->l3. This is relevant + * for CN pooling, to decide which CN link to map the RUA context to. */ +int hnbgw_peek_l3(struct hnbgw_context_map *map, struct msgb *ranap_msg) +{ + ranap_message *message = hnbgw_decode_ranap_co(ranap_msg); + if (!message) { + LOGP(DCN, LOGL_ERROR, "Failed to decode RANAP PDU\n"); + return -EINVAL; + } + + switch (message->procedureCode) { + case RANAP_ProcedureCode_id_InitialUE_Message: + return peek_l3_initial_ue(map, &message->msg.initialUE_MessageIEs); + default: + LOGP(DCN, LOGL_ERROR, "unexpected RANAP PDU in RUA Connect message: %s\n", + get_value_string(ranap_procedure_code_vals, message->procedureCode)); + return -ENOTSUP; + } +}
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/src/osmo-hnbgw/hnbgw_rua.c -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/src/osmo-hnbgw/hnbgw_rua.c
Changed
@@ -38,19 +38,9 @@ #include <osmocom/rua/rua_common.h> #include <osmocom/rua/rua_ies_defs.h> #include <osmocom/hnbgw/context_map.h> +#include <osmocom/hnbgw/hnbgw_rua.h> #include <osmocom/hnbap/HNBAP_CN-DomainIndicator.h> - -static const char *cn_domain_indicator_to_str(RUA_CN_DomainIndicator_t cN_DomainIndicator) -{ - switch (cN_DomainIndicator) { - case RUA_CN_DomainIndicator_cs_domain: - return "IuCS"; - case RUA_CN_DomainIndicator_ps_domain: - return "IuPS"; - default: - return "(unknown-domain)"; - } -} +#include <osmocom/ranap/ranap_ies_defs.h> static int hnbgw_rua_tx(struct hnb_context *ctx, struct msgb *msg) { @@ -208,7 +198,11 @@ map = context_map_alloc(hnb, rua_ctx_id, is_ps); OSMO_ASSERT(map); - /* FUTURE: extract mobile identity and store in map-> */ + if (hnbgw_peek_l3(map, ranap_msg)) + LOGP(DCN, LOGL_NOTICE, "Failed to extract Mobile Identity from RUA Connect message's RANAP payload\n"); + /* map->l3 now contains all the interesting information from the NAS PDU, if any. + * If no useful information could be decoded, still continue to select a hopefully adequate link by round robin. + */ cnlink = hnbgw_cnlink_select(map); if (!cnlink) { @@ -223,7 +217,6 @@ return NULL; } - LOG_MAP(map, DCN, LOGL_INFO, "establishing SCCP link: selected %s\n", cnlink->name); return map; } @@ -366,7 +359,7 @@ context_id = asn1bitstr_to_u24(&ies.context_ID); LOGHNB(hnb, DRUA, LOGL_DEBUG, "RUA %s Connect.req(ctx=0x%x, %s)\n", - cn_domain_indicator_to_str(ies.cN_DomainIndicator), context_id, + ranap_domain_name(ies.cN_DomainIndicator), context_id, ies.establishment_Cause == RUA_Establishment_Cause_emergency_call ? "emergency" : "normal"); rc = rua_to_scu(hnb, ies.cN_DomainIndicator, RUA_ProcedureCode_id_Connect,
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/src/osmo-hnbgw/hnbgw_vty.c -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/src/osmo-hnbgw/hnbgw_vty.c
Changed
@@ -281,6 +281,28 @@ return CMD_SUCCESS; } +DEFUN(cfg_hnbgw_plmn, cfg_hnbgw_plmn_cmd, + "plmn <1-999> <0-999>", + "Configure the HNBGW's PLMN. The PLMN is transmitted in RANAP RESET towards the CN.\n" + "MCC, Mobile Country Code\n" + "MNC, Mobile Network Code\n") +{ + struct osmo_plmn_id plmn; + + if (osmo_mcc_from_str(argv0, &plmn.mcc)) { + vty_out(vty, "%% Error decoding MCC: %s%s", argv0, VTY_NEWLINE); + return CMD_WARNING; + } + + if (osmo_mnc_from_str(argv1, &plmn.mnc, &plmn.mnc_3_digits)) { + vty_out(vty, "%% Error decoding MNC: %s%s", argv1, VTY_NEWLINE); + return CMD_WARNING; + } + + g_hnbgw->config.plmn = plmn; + return CMD_SUCCESS; +} + DEFUN(cfg_hnbgw_rnc_id, cfg_hnbgw_rnc_id_cmd, "rnc-id <0-65535>", "Configure the HNBGW's RNC Id, the common RNC Id used for all connected hNodeB. It is sent to" @@ -710,6 +732,35 @@ return CMD_SUCCESS; } +DEFUN(cnlink_ranap_reset, cnlink_ranap_reset_cmd, + "(msc|sgsn) " CNLINK_NR_RANGE " ranap reset", + "Manipulate an IuCS link to an MSC\n" + "Manipulate an IuPS link to an SGSN\n" + "MSC/SGSN nr\n" + "Manipulate RANAP layer of Iu-interface\n" + "Flip this CN link to disconnected state and re-send RANAP RESET\n") +{ + struct hnbgw_cnpool *cnpool; + struct hnbgw_cnlink *cnlink; + const char *msc_sgsn = argv0; + int nr = atoi(argv1); + + if (!strcmp("msc", msc_sgsn)) + cnpool = &g_hnbgw->sccp.cnpool_iucs; + else + cnpool = &g_hnbgw->sccp.cnpool_iups; + + cnlink = cnlink_get_nr(cnpool, nr, false); + if (!cnlink) { + vty_out(vty, "%% No such %s: nr %d\n", msc_sgsn, nr); + return CMD_WARNING; + } + + LOG_CNLINK(cnlink, DCN, LOGL_NOTICE, "VTY requests BSSMAP RESET\n"); + cnlink_resend_reset(cnlink); + return CMD_SUCCESS; +} + #define APPLY_STR "Immediately use configuration modified via telnet VTY, and restart components as needed.\n" #define SCCP_RESTART_STR \ " If 'remote-addr' changed, related SCCP links will be restarted, possibly dropping active UE contexts." @@ -820,6 +871,12 @@ { vty_out(vty, "hnbgw%s", VTY_NEWLINE); + if (g_hnbgw->config.plmn.mcc) + vty_out(vty, " plmn %s %s%s", + osmo_mcc_name_c(OTC_SELECT, g_hnbgw->config.plmn.mcc), + osmo_mnc_name_c(OTC_SELECT, g_hnbgw->config.plmn.mnc, g_hnbgw->config.plmn.mnc_3_digits), + VTY_NEWLINE); + vty_out(vty, " rnc-id %u%s", g_hnbgw->config.rnc_id, VTY_NEWLINE); vty_out(vty, " log-prefix %s%s", g_hnbgw->config.log_prefix_hnb_id ? "hnb-id" : "umts-cell-id", @@ -912,6 +969,7 @@ install_element(CONFIG_NODE, &cfg_hnbgw_cmd); install_node(&hnbgw_node, config_write_hnbgw); + install_element(HNBGW_NODE, &cfg_hnbgw_plmn_cmd); install_element(HNBGW_NODE, &cfg_hnbgw_rnc_id_cmd); install_element(HNBGW_NODE, &cfg_hnbgw_log_prefix_cmd); install_element(HNBGW_NODE, &cfg_hnbgw_max_sccp_cr_payload_len_cmd); @@ -976,4 +1034,5 @@ install_element_ve(&show_nri_cmd); install_element(ENABLE_NODE, &cnpool_roundrobin_next_cmd); + install_element(ENABLE_NODE, &cnlink_ranap_reset_cmd); }
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/src/osmo-hnbgw/osmo_hnbgw_main.c -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/src/osmo-hnbgw/osmo_hnbgw_main.c
Changed
@@ -25,6 +25,7 @@ #include <osmocom/core/application.h> #include <osmocom/core/logging.h> +#include <osmocom/core/stats.h> #include <osmocom/vty/vty.h> #include <osmocom/vty/command.h> @@ -32,6 +33,7 @@ #include <osmocom/vty/misc.h> #include <osmocom/vty/telnet_interface.h> #include <osmocom/vty/ports.h> +#include <osmocom/vty/stats.h> #include <osmocom/ctrl/control_vty.h> #include <osmocom/ctrl/ports.h> @@ -194,6 +196,13 @@ if (rc < 0) exit(1); + osmo_stats_init(g_hnbgw); + rc = rate_ctr_init(g_hnbgw); + if (rc) { + LOGP(DMAIN, LOGL_FATAL, "rate_ctr_init() failed with rc=%d\n", rc); + exit(1); + } + osmo_fsm_log_timeouts(true); rc = osmo_ss7_init(); @@ -211,6 +220,7 @@ ctrl_vty_init(g_hnbgw); logging_vty_add_cmds(); osmo_talloc_vty_add_cmds(); + osmo_stats_vty_add_cmds(); /* Handle options after vty_init(), for --version */ handle_options(argc, argv);
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/src/osmo-hnbgw/tdefs.c -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/src/osmo-hnbgw/tdefs.c
Changed
@@ -32,6 +32,8 @@ }; struct osmo_tdef hnbgw_T_defs = { + {.T = 3113, .default_val = 15, .desc = "Time to keep Paging record, for CN pools with more than one link" }, + {.T = 4, .default_val = 5, .desc = "Timeout to receive RANAP RESET ACKNOWLEDGE from an MSC/SGSN" }, {.T = -31, .default_val = 5, .desc = "Timeout for discarding a partially released context map (RUA <-> SCCP)" }, {.T = -1002, .default_val = 10, .desc = "Timeout for the HNB to respond to PS RAB Assignment Request" }, { }
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/tests/Makefile.am -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/tests/Makefile.am
Changed
@@ -26,6 +26,7 @@ $(TESTSUITE) \ osmo-hnbgw-vty-test.cfg \ $(srcdir)/*.vty \ + $(srcdir)/*.ctrl \ $(srcdir)/config/*.cfg \ $(srcdir)/config/*.vty \ $(srcdir)/config/run_tests.sh \ @@ -68,6 +69,18 @@ config-tests: $(srcdir)/config/run_tests.sh "$(top_builddir)/src/osmo-hnbgw/osmo-hnbgw" "$(srcdir)/config/" $U +# Run a specific test with: 'make ctrl-test CTRL_TEST=osmo-hnbgw.ctrl' +CTRL_TEST ?= *.ctrl + +# To update the CTRL script from current application behavior, +# pass -u to ctrl_script_runner.py by doing: +# make ctrl-test U=-u +ctrl-test: $(top_builddir)/src/osmo-hnbgw/osmo-hnbgw + osmo_verify_transcript_ctrl.py -v \ + -p 4262 \ + -r "$(top_builddir)/src/osmo-hnbgw/osmo-hnbgw -c $(top_srcdir)/doc/examples/osmo-hnbgw/osmo-hnbgw-cnpool.cfg" \ + $(U) $(srcdir)/$(CTRL_TEST) + check-local: atconfig $(TESTSUITE) $(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS) $(MAKE) $(AM_MAKEFLAGS) python-tests
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/tests/cnpool.vty -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/tests/cnpool.vty
Changed
@@ -1,4 +1,26 @@ OsmoHNBGW> enable +OsmoHNBGW# list +... + (msc|sgsn) <0-1000> ranap reset +... +OsmoHNBGW# msc? + msc Manipulate an IuCS link to an MSC +OsmoHNBGW# msc ? + <0-1000> MSC/SGSN nr +OsmoHNBGW# msc 0 ? + ranap Manipulate RANAP layer of Iu-interface +OsmoHNBGW# msc 0 ranap ? + reset Flip this CN link to disconnected state and re-send RANAP RESET + +OsmoHNBGW# sgsn? + sgsn Manipulate an IuPS link to an SGSN +OsmoHNBGW# sgsn ? + <0-1000> MSC/SGSN nr +OsmoHNBGW# sgsn 0 ? + ranap Manipulate RANAP layer of Iu-interface +OsmoHNBGW# sgsn 0 ranap ? + reset Flip this CN link to disconnected state and re-send RANAP RESET + OsmoHNBGW# configure terminal OsmoHNBGW(config)# ### cnpool doc strings
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/tests/config/defaults.vty -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/tests/config/defaults.vty
Changed
@@ -30,3 +30,6 @@ iuh mgw 0 ... +msc 0 +sgsn 0 +...
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/tests/config/one_cs7.vty -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/tests/config/one_cs7.vty
Changed
@@ -24,3 +24,6 @@ point-code 1.1.1 hnbgw ... +msc 0 +sgsn 0 +...
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/tests/config/two_cs7.vty -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/tests/config/two_cs7.vty
Changed
@@ -30,3 +30,6 @@ point-code 2.2.2 hnbgw ... +msc 0 +sgsn 0 +...
View file
osmo-hnbgw_1.4.0.86.ee1a.tar.xz/tests/osmo-hnbgw.vty -> osmo-hnbgw_1.4.0.105.3a084.tar.xz/tests/osmo-hnbgw.vty
Changed
@@ -11,6 +11,7 @@ OsmoHNBGW(config)# hnbgw OsmoHNBGW(config-hnbgw)# list ... + plmn <1-999> <0-999> rnc-id <0-65535> log-prefix (hnb-id|umts-cell-id) iuh @@ -18,6 +19,46 @@ iups ... +OsmoHNBGW(config-hnbgw)# plmn? + plmn Configure the HNBGW's PLMN. The PLMN is transmitted in RANAP RESET towards the CN. +OsmoHNBGW(config-hnbgw)# plmn ? + <1-999> MCC, Mobile Country Code +OsmoHNBGW(config-hnbgw)# plmn 1 ? + <0-999> MNC, Mobile Network Code +OsmoHNBGW(config-hnbgw)# show running-config +... !plmn +OsmoHNBGW(config-hnbgw)# plmn 001 01 +OsmoHNBGW(config-hnbgw)# show running-config +... +hnbgw +... + plmn 001 01 +... + +OsmoHNBGW(config-hnbgw)# plmn 001 001 +OsmoHNBGW(config-hnbgw)# show running-config +... +hnbgw +... + plmn 001 001 +... + +OsmoHNBGW(config-hnbgw)# plmn 999 999 +OsmoHNBGW(config-hnbgw)# show running-config +... +hnbgw +... + plmn 999 999 +... + +OsmoHNBGW(config-hnbgw)# plmn 23 42 +OsmoHNBGW(config-hnbgw)# show running-config +... +hnbgw +... + plmn 023 42 +... + OsmoHNBGW(config-hnbgw)# rnc-id? rnc-id Configure the HNBGW's RNC Id, the common RNC Id used for all connected hNodeB. It is sent to each hNodeB upon HNBAP HNB-Register-Accept, and the hNodeB will subsequently send this as RANAP InitialUE Messages' GlobalRNC-ID IE. Takes effect as soon as the hNodeB re-registers.
View file
osmo-hnbgw_1.4.0.105.3a084.tar.xz/tests/test_nodes.ctrl
Added
@@ -0,0 +1,14 @@ +GET 1 rate_ctr.abs.iucs.0.cnpool:subscr:no_cnlink +GET_REPLY 1 rate_ctr.abs.iucs.0.cnpool:subscr:no_cnlink 0 + +GET 2 rate_ctr.abs.iups.0.cnpool:emerg:lost +GET_REPLY 2 rate_ctr.abs.iups.0.cnpool:emerg:lost 0 + +GET 3 rate_ctr.abs.msc.0.cnpool:subscr:new +GET_REPLY 3 rate_ctr.abs.msc.0.cnpool:subscr:new 0 + +GET 4 rate_ctr.abs.sgsn.0.cnpool:emerg:forwarded +GET_REPLY 4 rate_ctr.abs.sgsn.0.cnpool:emerg:forwarded 0 + +GET 5 rate_ctr.abs.msc.1.cnpool:subscr:new +GET_REPLY 5 rate_ctr.abs.msc.1.cnpool:subscr:new 0
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.