diff --git a/include/osmocom/hnbgw/context_map.h b/include/osmocom/hnbgw/context_map.h index 7bcdf57..b64358a 100644 --- a/include/osmocom/hnbgw/context_map.h +++ b/include/osmocom/hnbgw/context_map.h @@ -2,6 +2,7 @@ #include #include +#include #define LOG_MAP(HNB_CTX_MAP, SUBSYS, LEVEL, FMT, ARGS...) \ LOGHNB((HNB_CTX_MAP) ? (HNB_CTX_MAP)->hnb_ctx : NULL, \ @@ -10,12 +11,56 @@ (HNB_CTX_MAP) ? ((HNB_CTX_MAP)->is_ps ? "PS" : "CS") : "NULL", \ ##ARGS) +/* All these events' data argument may either be NULL, or point to a RANAP msgb. + * - The msgb shall be in the OTC_SELECT talloc pool, so that they will be deallocated automatically. Some events + * processing will store the msgb for later, in which case it will take over ownership of the msgb by means of + * talloc_steal(). + * - For events that may send a RANAP message towards CN via SCCP, the msgb shall have reserved headroom to fit a struct + * osmo_scu_prim. These are: MAP_RUA_EV_RX_*. + * - The RANAP message shall be at msgb_l2(). + */ +enum map_rua_fsm_event { + /* Receiving a RUA Connect from HNB. */ + MAP_RUA_EV_RX_CONNECT, + /* Receiving some data from HNB via RUA, to forward via SCCP to CN. */ + MAP_RUA_EV_RX_DIRECT_TRANSFER, + /* Receiving a RUA Disconnect from HNB. */ + MAP_RUA_EV_RX_DISCONNECT, + /* SCCP has received some data from CN to forward via RUA to HNB. */ + MAP_RUA_EV_TX_DIRECT_TRANSFER, + /* The CN side is disconnected (e.g. received an SCCP Released), that means we are going gracefully disconnect + * RUA, too. */ + MAP_RUA_EV_CN_DISC, + /* All of a sudden, there is no RUA link. For example, HNB vanished / restarted, or SCTP SHUTDOWN on the RUA + * link. Skip RUA disconnect. */ + MAP_RUA_EV_HNB_LINK_LOST, +}; + +/* All these events' data argument is identical to enum map_rua_fsm_event, with this specialisation: + * - The events that may send a RANAP message towards CN via SCCP and hence require a headroom for an osmo_scu_prim are: + * MAP_SCCP_EV_TX_DATA_REQUEST, MAP_SCCP_EV_RAN_DISC. + */ +enum map_sccp_fsm_event { + /* Receiving an SCCP CC from CN. */ + MAP_SCCP_EV_RX_CONNECTION_CONFIRM, + /* Receiving some data from CN via SCCP, to forward via RUA to HNB. */ + MAP_SCCP_EV_RX_DATA_INDICATION, + /* RUA has received some data from HNB to forward via SCCP to CN. */ + MAP_SCCP_EV_TX_DATA_REQUEST, + /* The RAN side received a Disconnect, that means we are going to expect SCCP to disconnect too. + * CN should have received an Iu-ReleaseComplete with or before this, give CN a chance to send an SCCP RLSD; + * after a timeout we will send a non-standard RLSD to the CN instead. */ + MAP_SCCP_EV_RAN_DISC, + /* Receiving an SCCP RLSD from CN, or libosmo-sigtran tells us about SCCP connection timeout. All done. */ + MAP_SCCP_EV_RX_RELEASED, +}; + +/* For context_map_get_state(), to combine the RUA and SCCP states, for VTY reporting only. */ enum hnbgw_context_map_state { - MAP_S_NULL, - MAP_S_ACTIVE, /* currently active map */ - MAP_S_RESERVED1, /* just disconnected, still resrved */ - MAP_S_RESERVED2, /* still reserved */ - MAP_S_NUM_STATES /* Number of states, keep this at the end */ + MAP_S_CONNECTING, /* not active yet; effectively waiting for SCCP CC */ + MAP_S_ACTIVE, /* both RUA and SCCP are connected */ + MAP_S_DISCONNECTING, /* not active anymore; effectively waiting for SCCP RLSD */ + MAP_S_NUM_STATES /* Number of states, keep this at the end */ }; extern const struct value_string hnbgw_context_map_state_names[]; @@ -28,27 +73,36 @@ struct hnbgw_cnlink; struct hnbgw_context_map { /* entry in the per-CN list of mappings */ struct llist_head cn_list; - /* entry in the per-HNB list of mappings. */ + /* entry in the per-HNB list of mappings. If hnb_ctx == NULL, then this llist entry has been llist_del()eted and + * must not be used. */ struct llist_head hnb_list; - /* Pointer to HNB for this map, to transceive RUA. */ + /* Backpointer to global hnb_gw. */ + struct hnb_gw *gw; + + /* Pointer to HNB for this map, to transceive RUA. If the HNB has disconnected without releasing the RUA + * context, this is NULL. */ struct hnb_context *hnb_ctx; /* RUA context ID used in RUA messages to/from the hnb_gw. */ uint32_t rua_ctx_id; + /* FSM handling the RUA state for rua_ctx_id. */ + struct osmo_fsm_inst *rua_fi; /* Pointer to CN, to transceive SCCP. */ struct hnbgw_cnlink *cn_link; /* SCCP User SAP connection ID used in SCCP messages to/from the cn_link. */ uint32_t scu_conn_id; - /* Set to true on SCCP Conn Conf, set to false when an OSMO_SCU_PRIM_N_DISCONNECT has been sent for the SCCP - * User SAP conn. Useful to avoid leaking SCCP connections: guarantee that an OSMO_SCU_PRIM_N_DISCONNECT gets - * sent, even when RUA fails to gracefully disconnect. */ - bool scu_conn_active; + /* FSM handling the SCCP state for scu_conn_id. */ + struct osmo_fsm_inst *sccp_fi; /* False for CS, true for PS */ bool is_ps; - enum hnbgw_context_map_state state; + /* 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 + * and immediately release it after that, to not leak the connection. */ + bool please_disconnect; /* FSM instance for the MGW, handles the async MGCP communication necessary to intercept CS RAB Assignment and * redirect the RTP via the MGW. */ @@ -71,17 +125,40 @@ struct hnbgw_context_map { /* All PS RABs and their GTP tunnel mappings. list of struct ps_rab. Each ps_rab FSM handles the PFCP * communication for one particular RAB ID. */ struct llist_head ps_rabs; + + /* Flag to prevent calling context_map_free() from cleanup code paths triggered by context_map_free() itself. */ + bool deallocating; }; +enum hnbgw_context_map_state context_map_get_state(struct hnbgw_context_map *map); +enum hnbgw_context_map_state map_rua_get_state(struct hnbgw_context_map *map); +enum hnbgw_context_map_state map_sccp_get_state(struct hnbgw_context_map *map); struct hnbgw_context_map * context_map_alloc_by_hnb(struct hnb_context *hnb, uint32_t rua_ctx_id, bool is_ps, struct hnbgw_cnlink *cn_if_new); +void map_rua_fsm_alloc(struct hnbgw_context_map *map); +void map_sccp_fsm_alloc(struct hnbgw_context_map *map); + struct hnbgw_context_map * context_map_by_cn(struct hnbgw_cnlink *cn, uint32_t scu_conn_id); void context_map_hnb_released(struct hnbgw_context_map *map); -int context_map_init(struct hnb_gw *gw); +#define map_rua_dispatch(MAP, EVENT, MSGB) \ + _map_rua_dispatch(MAP, EVENT, MSGB, __FILE__, __LINE__) +int _map_rua_dispatch(struct hnbgw_context_map *map, uint32_t event, struct msgb *ranap_msg, + const char *file, int line); + +#define map_sccp_dispatch(MAP, EVENT, MSGB) \ + _map_sccp_dispatch(MAP, EVENT, MSGB, __FILE__, __LINE__) +int _map_sccp_dispatch(struct hnbgw_context_map *map, uint32_t event, struct msgb *ranap_msg, + const char *file, int line); + +bool map_rua_is_active(struct hnbgw_context_map *map); +bool map_sccp_is_active(struct hnbgw_context_map *map); +void context_map_check_released(struct hnbgw_context_map *map); + +unsigned int msg_has_l2_data(const struct msgb *msg); diff --git a/include/osmocom/hnbgw/hnbgw.h b/include/osmocom/hnbgw/hnbgw.h index f1b107e..96a2ef3 100644 --- a/include/osmocom/hnbgw/hnbgw.h +++ b/include/osmocom/hnbgw/hnbgw.h @@ -19,6 +19,8 @@ enum { DRUA, DRANAP, DMGW, + DHNB, + DCN, }; #define LOGHNB(HNB_CTX, ss, lvl, fmt, args ...) \ @@ -201,3 +203,5 @@ static inline bool hnb_gw_is_gtp_mapping_enabled(const struct hnb_gw *gw) { return gw->config.pfcp.remote_addr != NULL; } + +struct msgb *hnbgw_ranap_msg_alloc(const char *name); diff --git a/include/osmocom/hnbgw/hnbgw_cn.h b/include/osmocom/hnbgw/hnbgw_cn.h index b481a69..0df2716 100644 --- a/include/osmocom/hnbgw/hnbgw_cn.h +++ b/include/osmocom/hnbgw/hnbgw_cn.h @@ -3,3 +3,5 @@ #include int hnbgw_cnlink_init(struct hnb_gw *gw, const char *stp_host, uint16_t stp_port, const char *local_ip); + +const struct osmo_sccp_addr *hnbgw_cn_get_remote_addr(struct hnb_gw *gw, bool is_ps); diff --git a/include/osmocom/hnbgw/mgw_fsm.h b/include/osmocom/hnbgw/mgw_fsm.h index 8b14eaa..d4e989d 100644 --- a/include/osmocom/hnbgw/mgw_fsm.h +++ b/include/osmocom/hnbgw/mgw_fsm.h @@ -2,6 +2,6 @@ #include -int handle_rab_ass_req(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message); -int mgw_fsm_handle_rab_ass_resp(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message); +int handle_rab_ass_req(struct hnbgw_context_map *map, struct msgb *ranap_msg, ranap_message *message); +int mgw_fsm_handle_rab_ass_resp(struct hnbgw_context_map *map, struct msgb *ranap_msg, ranap_message *message); int mgw_fsm_release(struct hnbgw_context_map *map); diff --git a/include/osmocom/hnbgw/ps_rab_ass_fsm.h b/include/osmocom/hnbgw/ps_rab_ass_fsm.h index 775d73a..0728b0d 100644 --- a/include/osmocom/hnbgw/ps_rab_ass_fsm.h +++ b/include/osmocom/hnbgw/ps_rab_ass_fsm.h @@ -9,6 +9,6 @@ enum ps_rab_ass_fsm_event { PS_RAB_ASS_EV_RAB_FAIL, }; -int hnbgw_gtpmap_rx_rab_ass_req(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message); -int hnbgw_gtpmap_rx_rab_ass_resp(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message); +int hnbgw_gtpmap_rx_rab_ass_req(struct hnbgw_context_map *map, struct msgb *ranap_msg, ranap_message *message); +int hnbgw_gtpmap_rx_rab_ass_resp(struct hnbgw_context_map *map, struct msgb *ranap_msg, ranap_message *message); void hnbgw_gtpmap_release(struct hnbgw_context_map *map); diff --git a/include/osmocom/hnbgw/tdefs.h b/include/osmocom/hnbgw/tdefs.h index 8ae1c97..6fee79b 100644 --- a/include/osmocom/hnbgw/tdefs.h +++ b/include/osmocom/hnbgw/tdefs.h @@ -4,4 +4,5 @@ extern struct osmo_tdef mgw_fsm_T_defs[]; extern struct osmo_tdef ps_T_defs[]; +extern struct osmo_tdef cmap_T_defs[]; extern struct osmo_tdef_group hnbgw_tdef_group[]; diff --git a/src/osmo-hnbgw/Makefile.am b/src/osmo-hnbgw/Makefile.am index a6cf5ea..d1179e4 100644 --- a/src/osmo-hnbgw/Makefile.am +++ b/src/osmo-hnbgw/Makefile.am @@ -37,6 +37,8 @@ osmo_hnbgw_SOURCES = \ hnbgw_ranap.c \ hnbgw_vty.c \ context_map.c \ + context_map_rua.c \ + context_map_sccp.c \ hnbgw_cn.c \ ranap_rab_ass.c \ mgw_fsm.c \ diff --git a/src/osmo-hnbgw/context_map.c b/src/osmo-hnbgw/context_map.c index 9816918..31f635d 100644 --- a/src/osmo-hnbgw/context_map.c +++ b/src/osmo-hnbgw/context_map.c @@ -34,13 +34,24 @@ #include const struct value_string hnbgw_context_map_state_names[] = { - {MAP_S_NULL , "not-initialized"}, - {MAP_S_ACTIVE , "active"}, - {MAP_S_RESERVED1, "inactive-reserved"}, - {MAP_S_RESERVED2, "inactive-discard"}, - {0, NULL} + { MAP_S_CONNECTING, "connecting" }, + { MAP_S_ACTIVE, "active" }, + { MAP_S_DISCONNECTING, "disconnecting" }, + {} }; +/* Combine the RUA and SCCP states, for VTY reporting only. */ +enum hnbgw_context_map_state context_map_get_state(struct hnbgw_context_map *map) +{ + enum hnbgw_context_map_state rua = map_rua_get_state(map); + enum hnbgw_context_map_state sccp = map_sccp_get_state(map); + if (rua == MAP_S_ACTIVE && sccp == MAP_S_ACTIVE) + return MAP_S_ACTIVE; + if (rua == MAP_S_DISCONNECTING || sccp == MAP_S_DISCONNECTING) + return MAP_S_DISCONNECTING; + return MAP_S_CONNECTING; +} + /* is a given SCCP USER SAP Connection ID in use for a given CN link? */ static int cn_id_in_use(struct hnbgw_cnlink *cn, uint32_t id) { @@ -92,11 +103,15 @@ context_map_alloc_by_hnb(struct hnb_context *hnb, uint32_t rua_ctx_id, uint32_t new_scu_conn_id; llist_for_each_entry(map, &hnb->map_list, hnb_list) { - if (map->state != MAP_S_ACTIVE) + if (map->cn_link != cn_if_new) continue; - if (map->cn_link != cn_if_new) { + + /* Matching on RUA context id -- only match for RUA context that has not been disconnected yet. If an + * inactive context map for a rua_ctx_id is still around, we may have two entries for the same + * rua_ctx_id around at the same time. That should only stay until its SCCP side is done releasing. */ + if (!map_rua_is_active(map)) continue; - } + if (map->rua_ctx_id == rua_ctx_id && map->is_ps == is_ps) { return map; @@ -113,7 +128,7 @@ context_map_alloc_by_hnb(struct hnb_context *hnb, uint32_t rua_ctx_id, /* allocate a new map entry. */ map = talloc_zero(hnb, struct hnbgw_context_map); - map->state = MAP_S_NULL; + map->gw = hnb->gw; map->cn_link = cn_if_new; map->hnb_ctx = hnb; map->rua_ctx_id = rua_ctx_id; @@ -122,14 +137,43 @@ context_map_alloc_by_hnb(struct hnb_context *hnb, uint32_t rua_ctx_id, INIT_LLIST_HEAD(&map->ps_rab_ass); INIT_LLIST_HEAD(&map->ps_rabs); + map_rua_fsm_alloc(map); + map_sccp_fsm_alloc(map); + /* put it into both lists */ llist_add_tail(&map->hnb_list, &hnb->map_list); llist_add_tail(&map->cn_list, &cn_if_new->map_list); - map->state = MAP_S_ACTIVE; return map; } +int _map_rua_dispatch(struct hnbgw_context_map *map, uint32_t event, struct msgb *ranap_msg, + const char *file, int line) +{ + OSMO_ASSERT(map); + if (!map->rua_fi) { + LOG_MAP(map, DRUA, LOGL_ERROR, "not ready to receive RUA events\n"); + return -EINVAL; + } + return _osmo_fsm_inst_dispatch(map->rua_fi, event, ranap_msg, file, line); +} + +int _map_sccp_dispatch(struct hnbgw_context_map *map, uint32_t event, struct msgb *ranap_msg, + const char *file, int line) +{ + OSMO_ASSERT(map); + if (!map->sccp_fi) { + LOG_MAP(map, DRUA, LOGL_ERROR, "not ready to receive SCCP events\n"); + return -EINVAL; + } + return _osmo_fsm_inst_dispatch(map->sccp_fi, event, ranap_msg, file, line); +} + +unsigned int msg_has_l2_data(const struct msgb *msg) +{ + return msg && msgb_l2(msg) ? msgb_l2len(msg) : 0; +} + /* Map from a CN + Connection ID to HNB + Context ID */ struct hnbgw_context_map * context_map_by_cn(struct hnbgw_cnlink *cn, uint32_t scu_conn_id) @@ -137,8 +181,12 @@ context_map_by_cn(struct hnbgw_cnlink *cn, uint32_t scu_conn_id) struct hnbgw_context_map *map; llist_for_each_entry(map, &cn->map_list, cn_list) { - if (map->state != MAP_S_ACTIVE) + /* Matching on SCCP conn id -- only match for SCCP conn that has not been disconnected yet. If an + * inactive context map for an scu_conn_id is still around, we may have two entries for the same + * scu_conn_id around at the same time. That should only stay until its RUA side is done releasing. */ + if (!map_sccp_is_active(map)) continue; + if (map->scu_conn_id == scu_conn_id) { return map; } @@ -152,23 +200,37 @@ context_map_by_cn(struct hnbgw_cnlink *cn, uint32_t scu_conn_id) void context_map_hnb_released(struct hnbgw_context_map *map) { - LOG_MAP(map, DMAIN, LOGL_INFO, "Deactivating\n"); + /* When a HNB disconnects from RUA, the hnb_context will be freed. This hnbgw_context_map was allocated as a + * child of the hnb_context and would also be deallocated along with the hnb_context. However, the SCCP side for + * this hnbgw_context_map may still be waiting for a graceful release (SCCP RLC). Move this hnbgw_context_map to + * the global hnb_gw talloc ctx, so it can stay around for graceful release / for SCCP timeout. + * + * We could also always allocate hnbgw_context_map under hnb_gw, but it is nice to see which hnb_context owns + * which hnbgw_context_map in a talloc report. + */ + talloc_steal(map->gw, map); - /* set the state to reserved. We still show up in the list and - * avoid re-allocation of the context-id until we are cleaned up - * by the context_map garbage collector timer */ + /* Tell RUA that the HNB is gone. SCCP release will follow via FSM events. */ + map_rua_dispatch(map, MAP_RUA_EV_HNB_LINK_LOST, NULL); +} - if (map->state != MAP_S_RESERVED2) - map->state = MAP_S_RESERVED1; - - /* Is SCCP still active and needs to be disconnected ungracefully? */ - if (map->scu_conn_active) { - osmo_sccp_tx_disconn(map->hnb_ctx->gw->sccp.cnlink->sccp_user, map->scu_conn_id, NULL, 0); - map->scu_conn_active = false; +void context_map_free(struct hnbgw_context_map *map) +{ + /* guard against FSM termination infinitely looping back here */ + if (map->deallocating) { + LOG_MAP(map, DMAIN, LOGL_DEBUG, "context_map_free(): already deallocating\n"); + return; } + map->deallocating = true; + + if (map->rua_fi) + osmo_fsm_inst_term(map->rua_fi, OSMO_FSM_TERM_REGULAR, NULL); + OSMO_ASSERT(map->rua_fi == NULL); + + if (map->sccp_fi) + osmo_fsm_inst_term(map->sccp_fi, OSMO_FSM_TERM_REGULAR, NULL); + OSMO_ASSERT(map->sccp_fi == NULL); - /* a possibly still existing MGW FSM must be terminated when the context - * map is deactivated. (this is a cornercase) */ if (map->mgw_fi) { mgw_fsm_release(map); OSMO_ASSERT(map->mgw_fi == NULL); @@ -177,46 +239,21 @@ void context_map_hnb_released(struct hnbgw_context_map *map) #if ENABLE_PFCP hnbgw_gtpmap_release(map); #endif + + if (map->cn_link) + llist_del(&map->cn_list); + if (map->hnb_ctx) + llist_del(&map->hnb_list); + + LOG_MAP(map, DMAIN, LOGL_INFO, "Deallocating\n"); + talloc_free(map); } -static struct osmo_timer_list context_map_tmr; - -static void context_map_tmr_cb(void *data) +void context_map_check_released(struct hnbgw_context_map *map) { - struct hnb_gw *gw = data; - struct hnbgw_cnlink *cn = gw->sccp.cnlink; - struct hnbgw_context_map *map, *next_map; - - DEBUGP(DMAIN, "Running context mapper garbage collection\n"); - llist_for_each_entry_safe(map, next_map, &cn->map_list, cn_list) { - switch (map->state) { - case MAP_S_RESERVED1: - /* first time we see this reserved - * entry: mark it for stage 2 */ - map->state = MAP_S_RESERVED2; - break; - case MAP_S_RESERVED2: - /* second time we see this reserved - * entry: remove it */ - LOG_MAP(map, DMAIN, LOGL_INFO, "Deallocating\n"); - map->state = MAP_S_NULL; - llist_del(&map->cn_list); - llist_del(&map->hnb_list); - talloc_free(map); - break; - default: - break; - } + if (map_rua_is_active(map) || map_sccp_is_active(map)) { + /* still active, do not release yet. */ + return; } - /* re-schedule this timer */ - osmo_timer_schedule(&context_map_tmr, EXPIRY_TIMER_SECS, 0); -} - -int context_map_init(struct hnb_gw *gw) -{ - context_map_tmr.cb = context_map_tmr_cb; - context_map_tmr.data = gw; - osmo_timer_schedule(&context_map_tmr, EXPIRY_TIMER_SECS, 0); - - return 0; + context_map_free(map); } diff --git a/src/osmo-hnbgw/context_map_rua.c b/src/osmo-hnbgw/context_map_rua.c new file mode 100644 index 0000000..25304d9 --- /dev/null +++ b/src/osmo-hnbgw/context_map_rua.c @@ -0,0 +1,361 @@ +/* RUA side FSM of hnbgw_context_map */ +/* (C) 2023 by sysmocom - s.m.f.c. GmbH + * All Rights Reserved + * + * SPDX-License-Identifier: AGPL-3.0+ + * + * 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 . + */ + +#include +#include + +#include + +#include +#include +#include +#include + +enum map_rua_fsm_state { + MAP_RUA_ST_INIT, + MAP_RUA_ST_CONNECTED, + MAP_RUA_ST_DISCONNECTED, +}; + +static const struct value_string map_rua_fsm_event_names[] = { + OSMO_VALUE_STRING(MAP_RUA_EV_RX_CONNECT), + OSMO_VALUE_STRING(MAP_RUA_EV_RX_DIRECT_TRANSFER), + OSMO_VALUE_STRING(MAP_RUA_EV_RX_DISCONNECT), + OSMO_VALUE_STRING(MAP_RUA_EV_TX_DIRECT_TRANSFER), + OSMO_VALUE_STRING(MAP_RUA_EV_CN_DISC), + OSMO_VALUE_STRING(MAP_RUA_EV_HNB_LINK_LOST), + {} +}; + +static struct osmo_fsm map_rua_fsm; + +static const struct osmo_tdef_state_timeout map_rua_fsm_timeouts[32] = { + [MAP_RUA_ST_INIT] = { .T = -31 }, + [MAP_RUA_ST_DISCONNECTED] = { .T = -31 }, +}; + +/* Transition to a state, using the T timer defined in map_rua_fsm_timeouts. + * Assumes local variable fi exists. */ +#define map_rua_fsm_state_chg(state) \ + OSMO_ASSERT(osmo_tdef_fsm_inst_state_chg(fi, state, \ + map_rua_fsm_timeouts, \ + cmap_T_defs, \ + 5) == 0) + +void map_rua_fsm_alloc(struct hnbgw_context_map *map) +{ + struct osmo_fsm_inst *fi = osmo_fsm_inst_alloc(&map_rua_fsm, map, map, LOGL_DEBUG, NULL); + OSMO_ASSERT(fi); + osmo_fsm_inst_update_id_f_sanitize(fi, '-', "%s-RUA-%u", hnb_context_name(map->hnb_ctx), map->rua_ctx_id); + + OSMO_ASSERT(map->rua_fi == NULL); + map->rua_fi = fi; + + /* trigger the timeout */ + map_rua_fsm_state_chg(MAP_RUA_ST_INIT); +} + +enum hnbgw_context_map_state map_rua_get_state(struct hnbgw_context_map *map) +{ + if (!map || !map->rua_fi) + return MAP_S_DISCONNECTING; + switch (map->rua_fi->state) { + case MAP_RUA_ST_INIT: + return MAP_S_CONNECTING; + case MAP_RUA_ST_CONNECTED: + return MAP_S_ACTIVE; + default: + case MAP_RUA_ST_DISCONNECTED: + return MAP_S_DISCONNECTING; + } +} + +bool map_rua_is_active(struct hnbgw_context_map *map) +{ + if (!map || !map->rua_fi) + return false; + switch (map->rua_fi->state) { + case MAP_RUA_ST_DISCONNECTED: + return false; + default: + return true; + } +} + +static int map_rua_fsm_timer_cb(struct osmo_fsm_inst *fi) +{ + /* Return 1 to terminate FSM instance, 0 to keep running */ + switch (fi->state) { + default: + map_rua_fsm_state_chg(MAP_RUA_ST_DISCONNECTED); + return 0; + + case MAP_RUA_ST_DISCONNECTED: + return 1; + } +} + +static int destruct_ranap_cn_rx_co_ies(ranap_message *ranap_message_p) +{ + ranap_cn_rx_co_free(ranap_message_p); + return 0; +} + +/* Dispatch RANAP message to SCCP, if any. */ +static int handle_rx_rua(struct hnbgw_context_map *map, struct msgb *ranap_msg) +{ + int rc; + if (!msg_has_l2_data(ranap_msg)) + return 0; + + /* See if it is a RAB Assignment Response message from RUA to SCCP, where we need to change the user plane + * information, for RTP mapping via MGW, or GTP mapping via UPF. */ + if (!map->is_ps) { + ranap_message *message = talloc_zero(OTC_SELECT, ranap_message); + rc = ranap_cn_rx_co_decode2(message, msgb_l2(ranap_msg), msgb_l2len(ranap_msg)); + if (rc == 0) { + talloc_set_destructor(message, destruct_ranap_cn_rx_co_ies); + + switch (message->procedureCode) { + case RANAP_ProcedureCode_id_RAB_Assignment: + /* mgw_fsm_handle_rab_ass_resp() takes ownership of prim->oph and (ranap) message */ + return mgw_fsm_handle_rab_ass_resp(map, ranap_msg, message); + } + } +#if ENABLE_PFCP + } else if (hnb_gw_is_gtp_mapping_enabled(map->hnb_gw)) { + /* map->is_ps == true and PFCP is enabled in osmo-hnbgw.cfg */ + ranap_message *message = talloc_zero(OTC_SELECT, ranap_message); + rc = ranap_cn_rx_co_decode2(message, msgb_l2(ranap_msg), msgb_l2len(ranap_msg)); + if (rc == 0) { + talloc_set_destructor(message, destruct_ranap_cn_rx_co_ies); + + switch (message->procedureCode) { + case RANAP_ProcedureCode_id_RAB_Assignment: + /* ps_rab_ass_fsm takes ownership of prim->oph and RANAP message */ + return hnbgw_gtpmap_rx_rab_ass_resp(map, ranap_msg, message); + } + } +#endif + } + + /* It was not a RAB Assignment Response that needed to be intercepted. Forward as-is to SCCP. */ + return map_sccp_dispatch(map, MAP_SCCP_EV_TX_DATA_REQUEST, ranap_msg); +} + +static int forward_ranap_to_rua(struct hnbgw_context_map *map, struct msgb *ranap_msg) +{ + int rc; + + if (!msg_has_l2_data(ranap_msg)) + return 0; + + if (!map->hnb_ctx) { + LOGPFSML(map->rua_fi, LOGL_ERROR, "Cannot transmit RUA DirectTransfer: HNB has disconnected\n"); + return -ENOTCONN; + } + + rc = rua_tx_dt(map->hnb_ctx, map->is_ps, map->rua_ctx_id, msgb_l2(ranap_msg), msgb_l2len(ranap_msg)); + if (rc) + LOGPFSML(map->rua_fi, LOGL_ERROR, "Failed to transmit RUA DirectTransfer to HNB\n"); + return rc; +} + +static void map_rua_init_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct hnbgw_context_map *map = fi->priv; + struct msgb *ranap_msg = data; + + switch (event) { + + case MAP_RUA_EV_RX_CONNECT: + map_rua_fsm_state_chg(MAP_RUA_ST_CONNECTED); + /* The Connect will never be a RAB Assignment response, so no need for handle_rx_rua() (which decodes + * the RANAP message to detect a RAB Assignment response). Just forward to SCCP as is. */ + map_sccp_dispatch(map, MAP_SCCP_EV_TX_DATA_REQUEST, ranap_msg); + return; + + case MAP_RUA_EV_RX_DISCONNECT: + case MAP_RUA_EV_CN_DISC: + case MAP_RUA_EV_HNB_LINK_LOST: + /* Unlikely that SCCP is active, but let the SCCP FSM decide about that. */ + handle_rx_rua(map, ranap_msg); + /* There is a reason to shut down this RUA connection. Super unlikely, we haven't even processed the + * MAP_RUA_EV_RX_CONNECT that created this FSM. Semantically, RUA is not connected, so we can + * directly go to MAP_RUA_ST_DISCONNECTED. */ + map_rua_fsm_state_chg(MAP_RUA_ST_DISCONNECTED); + break; + + default: + OSMO_ASSERT(false); + } +} + +static void map_rua_tx_disconnect(struct osmo_fsm_inst *fi) +{ + struct hnbgw_context_map *map = fi->priv; + RUA_Cause_t rua_cause; + + if (!map->hnb_ctx || !map->hnb_ctx->conn) { + /* HNB already disconnected, nothing to do. */ + LOGPFSML(fi, LOGL_NOTICE, "HNB vanished, this RUA context cannot disconnect gracefully\n"); + return; + } + + /* Send Disconnect to RUA without RANAP data. */ + rua_cause = (RUA_Cause_t){ + .present = RUA_Cause_PR_radioNetwork, + .choice.radioNetwork = RUA_CauseRadioNetwork_network_release, + }; + 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"); +} + +static void map_rua_connected_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct hnbgw_context_map *map = fi->priv; + struct msgb *ranap_msg = data; + + switch (event) { + + case MAP_RUA_EV_RX_DIRECT_TRANSFER: + /* received DirectTransfer from RUA, forward to SCCP */ + handle_rx_rua(map, ranap_msg); + return; + + case MAP_RUA_EV_TX_DIRECT_TRANSFER: + /* Someone (usually the SCCP side) wants us to send a RANAP payload to HNB via RUA */ + forward_ranap_to_rua(map, ranap_msg); + return; + + case MAP_RUA_EV_RX_DISCONNECT: + /* received Disconnect from RUA. forward any payload to SCCP, and change state. */ + if (!map_sccp_is_active(map)) { + /* If, unlikely, the SCCP is already gone, changing to MAP_RUA_ST_DISCONNECTED frees the + * hnbgw_context_map. Avoid a use-after-free. */ + map_rua_fsm_state_chg(MAP_RUA_ST_DISCONNECTED); + return; + } + map_rua_fsm_state_chg(MAP_RUA_ST_DISCONNECTED); + handle_rx_rua(map, ranap_msg); + return; + + case MAP_RUA_EV_HNB_LINK_LOST: + /* The HNB is gone. Cannot gracefully cleanup the RUA connection, just be gone. */ + map_rua_fsm_state_chg(MAP_RUA_ST_DISCONNECTED); + return; + + case MAP_RUA_EV_CN_DISC: + /* There is a disruptive reason to shut down this RUA connection, HNB is still there */ + OSMO_ASSERT(data == NULL); + map_rua_tx_disconnect(fi); + map_rua_fsm_state_chg(MAP_RUA_ST_DISCONNECTED); + return; + + default: + OSMO_ASSERT(false); + } +} + +static void map_rua_disconnected_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct hnbgw_context_map *map = fi->priv; + /* For sanity, always tell SCCP to disconnect, if it hasn't done so. Dispatching MAP_SCCP_EV_RAN_DISC may send + * SCCP into MAP_RUA_ST_DISCONNECTED, which calls context_map_check_released() and frees the hnbgw_context_map, + * so don't free it a second time. If SCCP stays active, calling context_map_check_released() has no effect. */ + if (map_sccp_is_active(map)) + map_sccp_dispatch(map, MAP_SCCP_EV_RAN_DISC, NULL); + else + context_map_check_released(map); +} + +static void map_rua_disconnected_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct msgb *ranap_msg = data; + if (msg_has_l2_data(ranap_msg)) + LOGPFSML(fi, LOGL_ERROR, "RUA not connected, cannot dispatch RANAP message\n"); +} + +void map_rua_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause) +{ + struct hnbgw_context_map *map = fi->priv; + map->rua_fi = NULL; + context_map_check_released(map); +} + +#define S(x) (1 << (x)) + +static const struct osmo_fsm_state map_rua_fsm_states[] = { + [MAP_RUA_ST_INIT] = { + .name = "init", + .in_event_mask = 0 + | S(MAP_RUA_EV_RX_CONNECT) + | S(MAP_RUA_EV_RX_DISCONNECT) + | S(MAP_RUA_EV_CN_DISC) + | S(MAP_RUA_EV_HNB_LINK_LOST) + , + .out_state_mask = 0 + | S(MAP_RUA_ST_INIT) + | S(MAP_RUA_ST_CONNECTED) + | S(MAP_RUA_ST_DISCONNECTED) + , + .action = map_rua_init_action, + }, + [MAP_RUA_ST_CONNECTED] = { + .name = "connected", + .in_event_mask = 0 + | S(MAP_RUA_EV_RX_DIRECT_TRANSFER) + | S(MAP_RUA_EV_TX_DIRECT_TRANSFER) + | S(MAP_RUA_EV_RX_DISCONNECT) + | S(MAP_RUA_EV_CN_DISC) + | S(MAP_RUA_EV_HNB_LINK_LOST) + , + .out_state_mask = 0 + | S(MAP_RUA_ST_DISCONNECTED) + , + .action = map_rua_connected_action, + }, + [MAP_RUA_ST_DISCONNECTED] = { + .name = "disconnected", + .in_event_mask = 0 + | S(MAP_RUA_EV_CN_DISC) + | S(MAP_RUA_EV_HNB_LINK_LOST) + , + .onenter = map_rua_disconnected_onenter, + .action = map_rua_disconnected_action, + }, +}; + +static struct osmo_fsm map_rua_fsm = { + .name = "map_rua", + .states = map_rua_fsm_states, + .num_states = ARRAY_SIZE(map_rua_fsm_states), + .log_subsys = DHNB, + .event_names = map_rua_fsm_event_names, + .timer_cb = map_rua_fsm_timer_cb, + .cleanup = map_rua_fsm_cleanup, +}; + +static __attribute__((constructor)) void map_rua_fsm_register(void) +{ + OSMO_ASSERT(osmo_fsm_register(&map_rua_fsm) == 0); +} diff --git a/src/osmo-hnbgw/context_map_sccp.c b/src/osmo-hnbgw/context_map_sccp.c new file mode 100644 index 0000000..3042636 --- /dev/null +++ b/src/osmo-hnbgw/context_map_sccp.c @@ -0,0 +1,546 @@ +/* SCCP side FSM of hnbgw_context_map */ +/* (C) 2023 by sysmocom - s.m.f.c. GmbH + * All Rights Reserved + * + * SPDX-License-Identifier: AGPL-3.0+ + * + * 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 . + */ + +#include +#include + +#include + +#include + +#include +#include +#include +#include + +enum map_sccp_fsm_state { + MAP_SCCP_ST_INIT, + MAP_SCCP_ST_WAIT_CC, + MAP_SCCP_ST_CONNECTED, + MAP_SCCP_ST_WAIT_RLSD, + MAP_SCCP_ST_DISCONNECTED, +}; + +static const struct value_string map_sccp_fsm_event_names[] = { + OSMO_VALUE_STRING(MAP_SCCP_EV_RX_CONNECTION_CONFIRM), + OSMO_VALUE_STRING(MAP_SCCP_EV_RX_DATA_INDICATION), + OSMO_VALUE_STRING(MAP_SCCP_EV_TX_DATA_REQUEST), + OSMO_VALUE_STRING(MAP_SCCP_EV_RAN_DISC), + OSMO_VALUE_STRING(MAP_SCCP_EV_RX_RELEASED), + {} +}; + +static struct osmo_fsm map_sccp_fsm; + +static const struct osmo_tdef_state_timeout map_sccp_fsm_timeouts[32] = { + [MAP_SCCP_ST_INIT] = { .T = -31 }, + [MAP_SCCP_ST_WAIT_CC] = { .T = -31 }, + [MAP_SCCP_ST_CONNECTED] = { .T = 0 }, + [MAP_SCCP_ST_WAIT_RLSD] = { .T = -31 }, + [MAP_SCCP_ST_DISCONNECTED] = { .T = -31 }, +}; + +/* Transition to a state, using the T timer defined in map_sccp_fsm_timeouts. + * Assumes local variable fi exists. */ +#define map_sccp_fsm_state_chg(state) \ + OSMO_ASSERT(osmo_tdef_fsm_inst_state_chg(fi, state, \ + map_sccp_fsm_timeouts, \ + cmap_T_defs, \ + 5) == 0) + +void map_sccp_fsm_alloc(struct hnbgw_context_map *map) +{ + 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-SCCP-%u", hnb_context_name(map->hnb_ctx), map->scu_conn_id); + + OSMO_ASSERT(map->sccp_fi == NULL); + map->sccp_fi = fi; + + /* trigger the timeout */ + map_sccp_fsm_state_chg(MAP_SCCP_ST_INIT); +} + +enum hnbgw_context_map_state map_sccp_get_state(struct hnbgw_context_map *map) +{ + if (!map || !map->sccp_fi) + return MAP_S_DISCONNECTING; + switch (map->sccp_fi->state) { + case MAP_SCCP_ST_INIT: + case MAP_SCCP_ST_WAIT_CC: + return MAP_S_CONNECTING; + case MAP_SCCP_ST_CONNECTED: + return MAP_S_ACTIVE; + default: + case MAP_SCCP_ST_WAIT_RLSD: + case MAP_SCCP_ST_DISCONNECTED: + return MAP_S_DISCONNECTING; + } +} + +bool map_sccp_is_active(struct hnbgw_context_map *map) +{ + if (!map || !map->sccp_fi) + return false; + switch (map->sccp_fi->state) { + case MAP_SCCP_ST_DISCONNECTED: + return false; + default: + return true; + } +} + +static int tx_sccp_cr(struct osmo_fsm_inst *fi, struct msgb *ranap_msg) +{ + struct hnbgw_context_map *map = fi->priv; + struct osmo_scu_prim *prim; + int rc; + + if (!ranap_msg) { + /* prepare a msgb to send an empty N-Connect prim (but this should never happen in practice) */ + ranap_msg = hnbgw_ranap_msg_alloc("SCCP-CR-empty"); + } + + prim = (struct osmo_scu_prim *)msgb_push(ranap_msg, sizeof(*prim)); + osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_REQUEST, ranap_msg); + prim->u.connect.called_addr = *hnbgw_cn_get_remote_addr(map->gw, map->is_ps); + prim->u.connect.calling_addr = map->gw->sccp.local_addr; + prim->u.connect.sccp_class = 2; + prim->u.connect.conn_id = map->scu_conn_id; + + rc = osmo_sccp_user_sap_down_nofree(map->cn_link->sccp_user, &prim->oph); + if (rc) + LOGPFSML(fi, LOGL_ERROR, "Failed to forward SCCP Connectoin Request to CN\n"); + return rc; +} + +static int tx_sccp_df1(struct osmo_fsm_inst *fi, struct msgb *ranap_msg) +{ + struct hnbgw_context_map *map = fi->priv; + struct osmo_scu_prim *prim; + int rc; + + if (!msg_has_l2_data(ranap_msg)) + return 0; + + prim = (struct osmo_scu_prim *)msgb_push(ranap_msg, sizeof(*prim)); + osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, ranap_msg); + prim->u.data.conn_id = map->scu_conn_id; + + rc = osmo_sccp_user_sap_down_nofree(map->cn_link->sccp_user, &prim->oph); + if (rc) + LOGPFSML(fi, LOGL_ERROR, "Failed to forward SCCP Data Form 1 to CN\n"); + return rc; +} + +static int tx_sccp_rlsd(struct osmo_fsm_inst *fi) +{ + struct hnbgw_context_map *map = fi->priv; + return osmo_sccp_tx_disconn(map->cn_link->sccp_user, map->scu_conn_id, NULL, 0); +} + +static int destruct_ranap_ran_rx_co_ies(ranap_message *ranap_message_p) +{ + ranap_ran_rx_co_free(ranap_message_p); + return 0; +} + +static int handle_rx_sccp(struct osmo_fsm_inst *fi, struct msgb *ranap_msg) +{ + struct hnbgw_context_map *map = fi->priv; + int rc; + + /* When there was no message received along with the received event, then there is nothing to forward to RUA. */ + if (!msg_has_l2_data(ranap_msg)) + return 0; + + /* See if it is a RAB Assignment Request message from SCCP to RUA, where we need to change the user plane + * information, for RTP mapping via MGW, or GTP mapping via UPF. */ + if (!map->is_ps) { + ranap_message *message; + /* Circuit-Switched. Set up mapping of RTP ports via MGW */ + message = talloc_zero(OTC_SELECT, ranap_message); + rc = ranap_ran_rx_co_decode(message, message, msgb_l2(ranap_msg), msgb_l2len(ranap_msg)); + + if (rc == 0) { + talloc_set_destructor(message, destruct_ranap_ran_rx_co_ies); + + switch (message->procedureCode) { + case RANAP_ProcedureCode_id_RAB_Assignment: + /* mgw_fsm_alloc_and_handle_rab_ass_req() takes ownership of (ranap) message */ + return handle_rab_ass_req(map, ranap_msg, message); + case RANAP_ProcedureCode_id_Iu_Release: + /* Any IU Release will terminate the MGW FSM, the message itsself is not passed to the + * FSM code. It is just forwarded normally by map_rua_tx_dt() below. */ + mgw_fsm_release(map); + break; + } + } +#if ENABLE_PFCP + } else { + ranap_message *message; + struct hnb_gw *hnb_gw = cnlink->gw; + /* Packet-Switched. Set up mapping of GTP ports via UPF */ + message = talloc_zero(OTC_SELECT, ranap_message); + rc = ranap_ran_rx_co_decode(message, message, msgb_l2(ranap_msg), msgb_l2len(ranap_msg)); + + if (rc == 0) { + talloc_set_destructor(message, destruct_ranap_ran_rx_co_ies); + + switch (message->procedureCode) { + + case RANAP_ProcedureCode_id_RAB_Assignment: + /* If a UPF is configured, handle the RAB Assignment via ps_rab_ass_fsm, and replace the + * GTP F-TEIDs in the RAB Assignment message before passing it on to RUA. */ + if (hnb_gw_is_gtp_mapping_enabled(hnb_gw)) { + LOGP(DMAIN, LOGL_DEBUG, + "RAB Assignment: setting up GTP tunnel mapping via UPF %s\n", + osmo_sockaddr_to_str_c(OTC_SELECT, &hnb_gw->pfcp.cp_peer->remote_addr)); + return hnbgw_gtpmap_rx_rab_ass_req(map, ranap_msg, message); + } + /* If no UPF is configured, directly forward the message as-is (no GTP mapping). */ + LOGP(DMAIN, LOGL_DEBUG, "RAB Assignment: no UPF configured, forwarding as-is\n"); + break; + + case RANAP_ProcedureCode_id_Iu_Release: + /* Any IU Release will terminate the MGW FSM, the message itsself is not passed to the + * FSM code. It is just forwarded normally by map_rua_tx_dt() below. */ + hnbgw_gtpmap_release(map); + break; + } + } +#endif + } + + /* It was not a RAB Assignment Request that needed to be intercepted. Forward as-is to RUA. */ + return map_rua_dispatch(map, MAP_RUA_EV_TX_DIRECT_TRANSFER, ranap_msg); +} + +static void map_sccp_init_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct hnbgw_context_map *map = fi->priv; + struct msgb *ranap_msg = data; + + switch (event) { + + case MAP_SCCP_EV_TX_DATA_REQUEST: + /* In the INIT state, the first MAP_SCCP_EV_TX_DATA_REQUEST will be the RANAP message received from the + * RUA Connect message. Send the SCCP CR and transition to WAIT_CC. */ + if (tx_sccp_cr(fi, ranap_msg) == 0) + map_sccp_fsm_state_chg(MAP_SCCP_ST_WAIT_CC); + return; + + case MAP_SCCP_EV_RAN_DISC: + /* 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"); + map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED); + return; + + case MAP_SCCP_EV_RX_RELEASED: + /* SCCP RLSD received from CN. This will never happen since we haven't even asked for a connection, but + * for completeness: */ + map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED); + return; + + default: + OSMO_ASSERT(false); + } +} + +static void map_sccp_wait_cc_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct hnbgw_context_map *map = fi->priv; + struct msgb *ranap_msg = data; + + switch (event) { + + case MAP_SCCP_EV_RX_CONNECTION_CONFIRM: + map_sccp_fsm_state_chg(MAP_SCCP_ST_CONNECTED); + /* Usually doesn't but if the SCCP CC contained data, forward it to RUA */ + handle_rx_sccp(fi, ranap_msg); + return; + + case MAP_SCCP_EV_TX_DATA_REQUEST: + LOGPFSML(fi, LOGL_ERROR, "Connection not yet confirmed, cannot forward RANAP to CN\n"); + return; + + case MAP_SCCP_EV_RAN_DISC: + /* 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"); + map->please_disconnect = true; + return; + + case MAP_SCCP_EV_RX_RELEASED: + /* SCCP RLSD received from CN. This will never happen since we haven't even received a Connection + * Confirmed, but for completeness: */ + handle_rx_sccp(fi, ranap_msg); + map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED); + return; + + default: + OSMO_ASSERT(false); + } +} + +static void map_sccp_connected_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct hnbgw_context_map *map = fi->priv; + if (map->please_disconnect) { + /* SCCP has already been asked to disconnect, so disconnect now that the CC has been received. Send RLSD + * to SCCP (without RANAP data) */ + tx_sccp_rlsd(fi); + map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED); + } +} + +static void map_sccp_connected_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct msgb *ranap_msg = data; + + switch (event) { + + case MAP_SCCP_EV_RX_DATA_INDICATION: + /* forward RANAP from SCCP to RUA */ + handle_rx_sccp(fi, ranap_msg); + return; + + case MAP_SCCP_EV_TX_DATA_REQUEST: + /* Someone (usually the RUA side) wants us to send a RANAP payload to CN via SCCP */ + tx_sccp_df1(fi, ranap_msg); + return; + + case MAP_SCCP_EV_RAN_DISC: + /* RUA has disconnected, and usually has sent an Iu-ReleaseComplete along with its RUA Disconnect. On + * SCCP, the Iu-ReleaseComplete should still be forwarded as N-Data (SCCP Data Form 1), and we will + * expect the CN to send an SCCP RLSD soon. */ + map_sccp_fsm_state_chg(MAP_SCCP_ST_WAIT_RLSD); + tx_sccp_df1(fi, ranap_msg); + return; + + case MAP_SCCP_EV_RX_RELEASED: + /* The CN sends an N-Disconnect (SCCP Released) out of the usual sequence. Not what we expected, but + * handle it. */ + LOGPFSML(fi, LOGL_ERROR, "CN sends SCCP Released sooner than expected\n"); + handle_rx_sccp(fi, ranap_msg); + map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED); + return; + + case MAP_SCCP_EV_RX_CONNECTION_CONFIRM: + /* Already connected. Unusual, but if there is data just forward it. */ + LOGPFSML(fi, LOGL_ERROR, "Already connected, but received SCCP CC again\n"); + handle_rx_sccp(fi, ranap_msg); + return; + + default: + OSMO_ASSERT(false); + } +} + +static void map_sccp_wait_rlsd_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct hnbgw_context_map *map = fi->priv; + /* For sanity, always tell RUA to disconnect, if it hasn't done so. */ + if (map_rua_is_active(map)) + map_rua_dispatch(map, MAP_RUA_EV_CN_DISC, NULL); +} + +static void map_sccp_wait_rlsd_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct msgb *ranap_msg = data; + + switch (event) { + + case MAP_SCCP_EV_RX_RELEASED: + /* The CN sends the expected SCCP RLSD. + * Usually there is no data, but if there is just forward it. + * Usually RUA is already disconnected, but let the RUA FSM decide about that. */ + handle_rx_sccp(fi, ranap_msg); + map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED); + return; + + case MAP_SCCP_EV_RX_DATA_INDICATION: + /* RUA is probably already disconnected, but let the RUA FSM decide about that. */ + handle_rx_sccp(fi, ranap_msg); + return; + + case MAP_SCCP_EV_TX_DATA_REQUEST: + case MAP_SCCP_EV_RAN_DISC: + /* Normally, RUA would already disconnected, but since SCCP is officially still connected, we can still + * forward messages there. Already waiting for CN to send the SCCP RLSD. If there is a message, forward + * it, and just continue to time out on the SCCP RLSD. */ + tx_sccp_df1(fi, ranap_msg); + return; + + case MAP_SCCP_EV_RX_CONNECTION_CONFIRM: + /* Already connected. Unusual, but if there is data just forward it. */ + LOGPFSML(fi, LOGL_ERROR, "Already connected, but received SCCP CC\n"); + handle_rx_sccp(fi, ranap_msg); + return; + + default: + OSMO_ASSERT(false); + } +} + +static void map_sccp_disconnected_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct hnbgw_context_map *map = fi->priv; + /* For sanity, always tell RUA to disconnect, if it hasn't done so. Dispatching MAP_RUA_EV_CN_DISC may send + * RUA into MAP_RUA_ST_DISCONNECTED, which calls context_map_check_released() and frees the hnbgw_context_map, + * so don't free it a second time. If RUA stays active, calling context_map_check_released() has no effect. */ + if (map_rua_is_active(map)) + map_rua_dispatch(map, MAP_RUA_EV_CN_DISC, NULL); + else + context_map_check_released(map); +} + +static void map_sccp_disconnected_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct msgb *ranap_msg = data; + + if (msg_has_l2_data(ranap_msg)) + LOGPFSML(fi, LOGL_ERROR, "SCCP not connected, cannot dispatch RANAP message\n"); +} + +static int map_sccp_fsm_timer_cb(struct osmo_fsm_inst *fi) +{ + struct hnbgw_context_map *map = fi->priv; + + /* Return 1 to terminate FSM instance, 0 to keep running */ + switch (fi->state) { + case MAP_SCCP_ST_INIT: + case MAP_SCCP_ST_WAIT_CC: + /* cannot sent SCCP RLSD, because the other side hasn't responded with the remote reference. */ + map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED); + return 0; + + case MAP_SCCP_ST_CONNECTED: + case MAP_SCCP_ST_WAIT_RLSD: + /* send SCCP RLSD. libosmo-sigtran/sccp_scoc.c will do the SCCP connection cleanup. + * (It will repeatedly send SCCP RLSD until the peer responded with SCCP RLC, or until the + * sccp_connection->t_int timer expires, and the sccp_connection is freed.) */ + if (map->cn_link && map->cn_link->sccp_user) + tx_sccp_rlsd(fi); + map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED); + return 0; + + default: + case MAP_SCCP_ST_DISCONNECTED: + return 1; + } +} + +void map_sccp_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause) +{ + struct hnbgw_context_map *map = fi->priv; + map->sccp_fi = NULL; +} + +#define S(x) (1 << (x)) + +static const struct osmo_fsm_state map_sccp_fsm_states[] = { + [MAP_SCCP_ST_INIT] = { + .name = "init", + .in_event_mask = 0 + | S(MAP_SCCP_EV_TX_DATA_REQUEST) + | S(MAP_SCCP_EV_RAN_DISC) + | S(MAP_SCCP_EV_RX_RELEASED) + , + .out_state_mask = 0 + | S(MAP_SCCP_ST_INIT) + | S(MAP_SCCP_ST_WAIT_CC) + | S(MAP_SCCP_ST_DISCONNECTED) + , + .action = map_sccp_init_action, + }, + [MAP_SCCP_ST_WAIT_CC] = { + .name = "wait_cc", + .in_event_mask = 0 + | S(MAP_SCCP_EV_RX_CONNECTION_CONFIRM) + | S(MAP_SCCP_EV_TX_DATA_REQUEST) + | S(MAP_SCCP_EV_RAN_DISC) + | S(MAP_SCCP_EV_RX_RELEASED) + , + .out_state_mask = 0 + | S(MAP_SCCP_ST_CONNECTED) + | S(MAP_SCCP_ST_DISCONNECTED) + , + .action = map_sccp_wait_cc_action, + }, + [MAP_SCCP_ST_CONNECTED] = { + .name = "connected", + .in_event_mask = 0 + | S(MAP_SCCP_EV_RX_DATA_INDICATION) + | S(MAP_SCCP_EV_TX_DATA_REQUEST) + | S(MAP_SCCP_EV_RAN_DISC) + | S(MAP_SCCP_EV_RX_RELEASED) + | S(MAP_SCCP_EV_RX_CONNECTION_CONFIRM) + , + .out_state_mask = 0 + | S(MAP_SCCP_ST_WAIT_RLSD) + | S(MAP_SCCP_ST_DISCONNECTED) + , + .onenter = map_sccp_connected_onenter, + .action = map_sccp_connected_action, + }, + [MAP_SCCP_ST_WAIT_RLSD] = { + .name = "wait_rlsd", + .in_event_mask = 0 + | S(MAP_SCCP_EV_RX_RELEASED) + | S(MAP_SCCP_EV_RX_DATA_INDICATION) + | S(MAP_SCCP_EV_TX_DATA_REQUEST) + | S(MAP_SCCP_EV_RAN_DISC) + | S(MAP_SCCP_EV_RX_CONNECTION_CONFIRM) + , + .out_state_mask = 0 + | S(MAP_SCCP_ST_DISCONNECTED) + , + .onenter = map_sccp_wait_rlsd_onenter, + .action = map_sccp_wait_rlsd_action, + }, + [MAP_SCCP_ST_DISCONNECTED] = { + .name = "disconnected", + .in_event_mask = 0 + | S(MAP_SCCP_EV_TX_DATA_REQUEST) + | S(MAP_SCCP_EV_RAN_DISC) + , + .onenter = map_sccp_disconnected_onenter, + .action = map_sccp_disconnected_action, + }, +}; + +static struct osmo_fsm map_sccp_fsm = { + .name = "map_sccp", + .states = map_sccp_fsm_states, + .num_states = ARRAY_SIZE(map_sccp_fsm_states), + .log_subsys = DCN, + .event_names = map_sccp_fsm_event_names, + .timer_cb = map_sccp_fsm_timer_cb, + .cleanup = map_sccp_fsm_cleanup, +}; + +static __attribute__((constructor)) void map_sccp_fsm_register(void) +{ + OSMO_ASSERT(osmo_fsm_register(&map_sccp_fsm) == 0); +} diff --git a/src/osmo-hnbgw/hnbgw.c b/src/osmo-hnbgw/hnbgw.c index 322dabe..ce82282 100644 --- a/src/osmo-hnbgw/hnbgw.c +++ b/src/osmo-hnbgw/hnbgw.c @@ -98,8 +98,6 @@ static struct hnb_gw *hnb_gw_create(void *ctx) INIT_LLIST_HEAD(&gw->hnb_list); INIT_LLIST_HEAD(&gw->ue_list); - context_map_init(gw); - gw->mgw_pool = mgcp_client_pool_alloc(gw); gw->config.mgcp_client = talloc_zero(tall_hnb_ctx, struct mgcp_client_conf); mgcp_client_conf_init(gw->config.mgcp_client); @@ -397,18 +395,16 @@ void hnb_context_release_ue_state(struct hnb_context *ctx) /* deactivate all context maps */ llist_for_each_entry_safe(map, map2, &ctx->map_list, hnb_list) { - /* remove it from list, as HNB context will soon be - * gone. Let's hope the second osmo_llist_del in the - * map garbage collector works fine? */ - llist_del(&map->hnb_list); - llist_del(&map->cn_list); context_map_hnb_released(map); + /* hnbgw_context_map will remove itself from lists when it is ready. */ } ue_context_free_by_hnb(ctx->gw, ctx); } void hnb_context_release(struct hnb_context *ctx) { + struct hnbgw_context_map *map; + LOGHNB(ctx, DMAIN, LOGL_INFO, "Releasing HNB context\n"); /* remove from the list of HNB contexts */ @@ -424,6 +420,14 @@ void hnb_context_release(struct hnb_context *ctx) osmo_stream_srv_destroy(ctx->conn); } /* else: we are called from closed_cb, so conn is being freed separately */ + /* hnbgw_context_map are still listed in ctx->map_list, but we are freeing ctx. Remove all entries from the + * list, but keep the hnbgw_context_map around for graceful release. They are also listed under + * hnbgw_cnlink->map_list, and will remove themselves when ready. */ + while ((map = llist_first_entry_or_null(&ctx->map_list, struct hnbgw_context_map, hnb_list))) { + llist_del(&map->hnb_list); + map->hnb_ctx = NULL; + } + talloc_free(ctx); } @@ -469,6 +473,16 @@ static const struct log_info_cat log_cat[] = { .color = "\033[1;33m", .description = "Media Gateway", }, + [DHNB] = { + .name = "DHNB", .loglevel = LOGL_NOTICE, .enabled = 1, + .color = OSMO_LOGCOLOR_CYAN, + .description = "HNB side (via RUA)", + }, + [DCN] = { + .name = "DCN", .loglevel = LOGL_NOTICE, .enabled = 1, + .color = OSMO_LOGCOLOR_DARKYELLOW, + .description = "Core Network side (via SCCP)", + }, }; static const struct log_info hnbgw_log_info = { @@ -841,3 +855,12 @@ int main(int argc, char **argv) /* not reached */ exit(0); } + +struct msgb *hnbgw_ranap_msg_alloc(const char *name) +{ + struct msgb *ranap_msg; + ranap_msg = msgb_alloc_c(OTC_SELECT, sizeof(struct osmo_scu_prim) + 1500, name); + msgb_reserve(ranap_msg, sizeof(struct osmo_scu_prim)); + ranap_msg->l2h = ranap_msg->data; + return ranap_msg; +} diff --git a/src/osmo-hnbgw/hnbgw_cn.c b/src/osmo-hnbgw/hnbgw_cn.c index 4c0e685..731c758 100644 --- a/src/osmo-hnbgw/hnbgw_cn.c +++ b/src/osmo-hnbgw/hnbgw_cn.c @@ -345,16 +345,13 @@ static int handle_cn_conn_conf(struct hnbgw_cnlink *cnlink, osmo_sccp_addr_to_str_c(OTC_SELECT, ss7, ¶m->calling_addr), osmo_sccp_addr_to_str_c(OTC_SELECT, ss7, ¶m->responding_addr)); - /* Nothing needs to happen for RUA, RUA towards the HNB doesn't seem to know any confirmations to its CONNECT - * operation. */ - map = context_map_by_cn(cnlink, param->conn_id); - if (!map) + if (!map) { + /* We have no such SCCP connection. Ignore. */ return 0; + } - /* SCCP connection is confirmed. Mark conn as active, i.e. requires a DISCONNECT to clean up the SCCP - * connection. */ - map->scu_conn_active = true; + map_sccp_dispatch(map, MAP_SCCP_EV_RX_CONNECTION_CONFIRM, oph->msg); return 0; } @@ -363,79 +360,14 @@ static int handle_cn_data_ind(struct hnbgw_cnlink *cnlink, struct osmo_prim_hdr *oph) { struct hnbgw_context_map *map; - ranap_message *message; - int rc; - - /* Usually connection-oriented data is always passed transparently towards the specific HNB, via a RUA - * connection identified by conn_id. An exception is made for RANAP RAB AssignmentRequest and - * RANAP RAB AssignmentResponse, since those messages contain transport layer information (RTP stream IP/Port), - * which is rewritten by the FSM that controls the co-located media gateway. */ map = context_map_by_cn(cnlink, param->conn_id); if (!map) { - /* FIXME: Return an error / released primitive */ + /* We have no such SCCP connection. Ignore. */ return 0; } - /* Intercept RAB Assignment Request, to map RTP and GTP between access and core */ - if (!map->is_ps) { - /* Circuit-Switched. Set up mapping of RTP ports via MGW */ - message = talloc_zero(map, ranap_message); - rc = ranap_ran_rx_co_decode(map, message, msgb_l2(oph->msg), msgb_l2len(oph->msg)); - - if (rc == 0) { - switch (message->procedureCode) { - case RANAP_ProcedureCode_id_RAB_Assignment: - /* mgw_fsm_alloc_and_handle_rab_ass_req() takes ownership of (ranap) message */ - return handle_rab_ass_req(map, oph, message); - case RANAP_ProcedureCode_id_Iu_Release: - /* Any IU Release will terminate the MGW FSM, the message itsself is not passed to the - * FSM code. It is just forwarded normally by the rua_tx_dt() call below. */ - mgw_fsm_release(map); - break; - } - ranap_ran_rx_co_free(message); - } - - talloc_free(message); -#if ENABLE_PFCP - } else { - struct hnb_gw *hnb_gw = cnlink->gw; - /* Packet-Switched. Set up mapping of GTP ports via UPF */ - message = talloc_zero(map, ranap_message); - rc = ranap_ran_rx_co_decode(map, message, msgb_l2(oph->msg), msgb_l2len(oph->msg)); - - if (rc == 0) { - switch (message->procedureCode) { - - case RANAP_ProcedureCode_id_RAB_Assignment: - /* If a UPF is configured, handle the RAB Assignment via ps_rab_ass_fsm, and replace the - * GTP F-TEIDs in the RAB Assignment message before passing it on to RUA. */ - if (hnb_gw_is_gtp_mapping_enabled(hnb_gw)) { - LOGP(DMAIN, LOGL_DEBUG, - "RAB Assignment: setting up GTP tunnel mapping via UPF %s\n", - osmo_sockaddr_to_str_c(OTC_SELECT, &hnb_gw->pfcp.cp_peer->remote_addr)); - return hnbgw_gtpmap_rx_rab_ass_req(map, oph, message); - } - /* If no UPF is configured, directly forward the message as-is (no GTP mapping). */ - LOGP(DMAIN, LOGL_DEBUG, "RAB Assignment: no UPF configured, forwarding as-is\n"); - break; - - case RANAP_ProcedureCode_id_Iu_Release: - /* Any IU Release will terminate the MGW FSM, the message itsself is not passed to the - * FSM code. It is just forwarded normally by the rua_tx_dt() call below. */ - hnbgw_gtpmap_release(map); - break; - } - ranap_ran_rx_co_free(message); - } - - talloc_free(message); -#endif - } - - return rua_tx_dt(map->hnb_ctx, map->is_ps, map->rua_ctx_id, - msgb_l2(oph->msg), msgb_l2len(oph->msg)); + return map_sccp_dispatch(map, MAP_SCCP_EV_RX_DATA_INDICATION, oph->msg); } static int handle_cn_disc_ind(struct hnbgw_cnlink *cnlink, @@ -449,22 +381,13 @@ static int handle_cn_disc_ind(struct hnbgw_cnlink *cnlink, LOGP(DMAIN, LOGL_DEBUG, "handle_cn_disc_ind() responding_addr=%s\n", inet_ntoa(param->responding_addr.ip.v4)); - RUA_Cause_t rua_cause = { - .present = RUA_Cause_PR_NOTHING, - /* FIXME: Convert incoming SCCP cause to RUA cause */ - }; - - /* we need to notify the HNB associated with this connection via - * a RUA DISCONNECT */ - map = context_map_by_cn(cnlink, param->conn_id); if (!map) { - /* FIXME: Return an error / released primitive */ + /* We have no connection. Ignore. */ return 0; } - return rua_tx_disc(map->hnb_ctx, map->is_ps, map->rua_ctx_id, - &rua_cause, msgb_l2(oph->msg), msgb_l2len(oph->msg)); + return map_sccp_dispatch(map, MAP_SCCP_EV_RX_RELEASED, oph->msg); } /* Entry point for primitives coming up from SCCP User SAP */ @@ -492,6 +415,8 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *ctx) return -1; } + talloc_steal(OTC_SELECT, oph->msg); + switch (OSMO_PRIM_HDR(oph)) { case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION): rc = handle_cn_unitdata(cnlink, &prim->u.unitdata, oph); @@ -512,8 +437,6 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *ctx) break; } - msgb_free(oph->msg); - return rc; } @@ -637,3 +560,8 @@ int hnbgw_cnlink_init(struct hnb_gw *gw, const char *stp_host, uint16_t stp_port return 0; } + +const struct osmo_sccp_addr *hnbgw_cn_get_remote_addr(struct hnb_gw *gw, bool is_ps) +{ + return is_ps ? &gw->sccp.iups_remote_addr : &gw->sccp.iucs_remote_addr; +} diff --git a/src/osmo-hnbgw/hnbgw_rua.c b/src/osmo-hnbgw/hnbgw_rua.c index 97ced4a..db7fdbb 100644 --- a/src/osmo-hnbgw/hnbgw_rua.c +++ b/src/osmo-hnbgw/hnbgw_rua.c @@ -62,6 +62,11 @@ static int hnbgw_rua_tx(struct hnb_context *ctx, struct msgb *msg) if (!msg) return -EINVAL; + if (!ctx || !ctx->conn) { + LOGHNB(ctx, DRUA, LOGL_ERROR, "RUA context to this HNB is not connected, cannot transmit message\n"); + return -ENOTCONN; + } + msgb_sctp_ppid(msg) = IUH_PPI_RUA; osmo_stream_srv_send(ctx->conn, msg); @@ -198,23 +203,16 @@ static int rua_to_scu(struct hnb_context *hnb, uint32_t context_id, uint32_t cause, const uint8_t *data, unsigned int len) { - struct msgb *msg; - struct osmo_scu_prim *prim; + struct msgb *ranap_msg = NULL; struct hnbgw_context_map *map = NULL; struct hnbgw_cnlink *cn = hnb->gw->sccp.cnlink; - struct osmo_sccp_addr *remote_addr; bool is_ps; - bool release_context_map = false; - ranap_message *message; - int rc; switch (cN_DomainIndicator) { case RUA_CN_DomainIndicator_cs_domain: - remote_addr = &hnb->gw->sccp.iucs_remote_addr; is_ps = false; break; case RUA_CN_DomainIndicator_ps_domain: - remote_addr = &hnb->gw->sccp.iups_remote_addr; is_ps = true; break; default: @@ -227,9 +225,15 @@ static int rua_to_scu(struct hnb_context *hnb, return 0; } - msg = msgb_alloc(1500, "rua_to_sccp"); - - prim = (struct osmo_scu_prim *) msgb_put(msg, sizeof(*prim)); + /* If there is RANAP data, include it in the msgb. In RUA there is always data in practice, but theoretically it + * could be an empty Connect or Disconnect. */ + if (data && len) { + /* According to API doc of map_rua_fsm_event: allocate msgb for RANAP data from OTC_SELECT, reserve + * headroom for an osmo_scu_prim. Point l2h at the RANAP data. */ + ranap_msg = hnbgw_ranap_msg_alloc("RANAP_from_RUA"); + ranap_msg->l2h = msgb_put(ranap_msg, len); + memcpy(ranap_msg->l2h, data, len); + } map = context_map_alloc_by_hnb(hnb, context_id, is_ps, cn); OSMO_ASSERT(map); @@ -237,91 +241,21 @@ static int rua_to_scu(struct hnb_context *hnb, LOG_MAP(map, DRUA, LOGL_DEBUG, "rx RUA %s with %u bytes RANAP data\n", rua_procedure_code_name(rua_procedure), data ? len : 0); - /* add primitive header */ switch (rua_procedure) { case RUA_ProcedureCode_id_Connect: - osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_REQUEST, msg); - prim->u.connect.called_addr = *remote_addr; - prim->u.connect.calling_addr = cn->gw->sccp.local_addr; - prim->u.connect.sccp_class = 2; - prim->u.connect.conn_id = map->scu_conn_id; - /* Two separate logs because of osmo_sccp_addr_dump(). */ - LOGHNB(hnb, DRUA, LOGL_DEBUG, "RUA to SCCP N_CONNECT: called_addr:%s\n", - osmo_sccp_addr_dump(&prim->u.connect.called_addr)); - LOGHNB(hnb, DRUA, LOGL_DEBUG, "RUA to SCCP N_CONNECT: calling_addr:%s\n", - osmo_sccp_addr_dump(&prim->u.connect.calling_addr)); - break; + return map_rua_dispatch(map, MAP_RUA_EV_RX_CONNECT, ranap_msg); case RUA_ProcedureCode_id_DirectTransfer: - osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, msg); - prim->u.data.conn_id = map->scu_conn_id; - break; + return map_rua_dispatch(map, MAP_RUA_EV_RX_DIRECT_TRANSFER, ranap_msg); case RUA_ProcedureCode_id_Disconnect: - osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_REQUEST, msg); - prim->u.disconnect.conn_id = map->scu_conn_id; - prim->u.disconnect.cause = cause; - release_context_map = true; - /* Mark SCCP conn as gracefully disconnected */ - map->scu_conn_active = false; - break; + return map_rua_dispatch(map, MAP_RUA_EV_RX_DISCONNECT, ranap_msg); default: /* No caller may ever pass a different RUA procedure code */ OSMO_ASSERT(false); } - - /* If there is RANAP data, include it in the msgb. Usually there is data, but this could also be an SCCP CR - * a.k.a. OSMO_SCU_PRIM_N_CONNECT without RANAP payload. */ - if (data && len) { - msg->l2h = msgb_put(msg, len); - memcpy(msg->l2h, data, len); - } - - /* If there is data, see if it is a RAB Assignment message where we need to change the user plane information, - * for RTP mapping via MGW (soon also GTP mapping via UPF). */ - if (data && len && map && !release_context_map) { - if (!map->is_ps) { - message = talloc_zero(map, ranap_message); - rc = ranap_cn_rx_co_decode2(message, msgb_l2(prim->oph.msg), msgb_l2len(prim->oph.msg)); - - if (rc == 0) { - switch (message->procedureCode) { - case RANAP_ProcedureCode_id_RAB_Assignment: - /* mgw_fsm_handle_rab_ass_resp() takes ownership of prim->oph and (ranap) message */ - return mgw_fsm_handle_rab_ass_resp(map, &prim->oph, message); - } - ranap_cn_rx_co_free(message); - } - - talloc_free(message); -#if ENABLE_PFCP - } else if (hnb_gw_is_gtp_mapping_enabled(hnb->gw)) { - /* map->is_ps == true and PFCP is enabled in osmo-hnbgw.cfg */ - message = talloc_zero(map, ranap_message); - rc = ranap_cn_rx_co_decode2(message, msgb_l2(prim->oph.msg), msgb_l2len(prim->oph.msg)); - - if (rc == 0) { - switch (message->procedureCode) { - case RANAP_ProcedureCode_id_RAB_Assignment: - /* ps_rab_ass_fsm takes ownership of prim->oph and RANAP message */ - return hnbgw_gtpmap_rx_rab_ass_resp(map, &prim->oph, message); - } - ranap_cn_rx_co_free(message); - } - - talloc_free(message); -#endif - } - } - - rc = osmo_sccp_user_sap_down(cn->sccp_user, &prim->oph); - - if (map && release_context_map) - context_map_hnb_released(map); - - return rc; } static uint32_t rua_to_scu_cause(RUA_Cause_t *in) diff --git a/src/osmo-hnbgw/hnbgw_vty.c b/src/osmo-hnbgw/hnbgw_vty.c index abddc79..7f79b82 100644 --- a/src/osmo-hnbgw/hnbgw_vty.c +++ b/src/osmo-hnbgw/hnbgw_vty.c @@ -208,10 +208,8 @@ static void vty_dump_hnb_info(struct vty *vty, struct hnb_context *hnb) hnb->hnbap_stream, hnb->rua_stream, VTY_NEWLINE); llist_for_each_entry(map, &hnb->map_list, hnb_list) { - map_count[map->is_ps? 1 : 0]++; - state_count[map->is_ps? 1 : 0] - [(map->state >= 0 && map->state < MAP_S_NUM_STATES)? - map->state : MAP_S_NUM_STATES]++; + map_count[map->is_ps ? 1 : 0]++; + state_count[map->is_ps ? 1 : 0][context_map_get_state(map)]++; } vty_dump_hnb_info__map_states(vty, "IuCS", map_count[0], state_count[0]); vty_dump_hnb_info__map_states(vty, "IuPS", map_count[1], state_count[1]); diff --git a/src/osmo-hnbgw/mgw_fsm.c b/src/osmo-hnbgw/mgw_fsm.c index fe1e227..cddc64e 100644 --- a/src/osmo-hnbgw/mgw_fsm.c +++ b/src/osmo-hnbgw/mgw_fsm.c @@ -52,10 +52,7 @@ /* Send Iu Release Request, this is done in erroneous cases from which we cannot recover */ static void tx_release_req(struct hnbgw_context_map *map) { - struct hnb_context *hnb = map->hnb_ctx; - struct hnbgw_cnlink *cn = hnb->gw->sccp.cnlink; struct msgb *msg; - struct osmo_scu_prim *prim; static const struct RANAP_Cause cause = { .present = RANAP_Cause_PR_transmissionNetwork, .choice.transmissionNetwork = @@ -64,11 +61,8 @@ static void tx_release_req(struct hnbgw_context_map *map) msg = ranap_new_msg_iu_rel_req(&cause); msg->l2h = msg->data; - - prim = (struct osmo_scu_prim *)msgb_push(msg, sizeof(*prim)); - prim->u.data.conn_id = map->scu_conn_id; - osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, msg); - osmo_sccp_user_sap_down(cn->sccp_user, &prim->oph); + talloc_steal(OTC_SELECT, msg); + map_sccp_dispatch(map, MAP_SCCP_EV_TX_DATA_REQUEST, msg); } #define S(x) (1 << (x)) @@ -112,7 +106,7 @@ struct mgw_fsm_priv { /* Pointers to messages and prim header we take ownership of */ ranap_message *ranap_rab_ass_req_message; ranap_message *ranap_rab_ass_resp_message; - struct osmo_prim_hdr *ranap_rab_ass_resp_oph; + struct msgb *ranap_rab_ass_resp_msgb; /* MGW context */ struct mgcp_client *mgcpc; @@ -173,7 +167,7 @@ static void mgw_fsm_crcx_hnb_onenter(struct osmo_fsm_inst *fi, uint32_t prev_sta mgw_info.codecs[0] = CODEC_IUFP; mgw_info.codecs_len = 1; - mgw_fsm_priv->mgcpc = mgcp_client_pool_get(map->hnb_ctx->gw->mgw_pool); + mgw_fsm_priv->mgcpc = mgcp_client_pool_get(map->gw->mgw_pool); if (!mgw_fsm_priv->mgcpc) { LOGPFSML(fi, LOGL_ERROR, "cannot ensure MGW endpoint -- no MGW configured, check configuration!\n"); @@ -255,8 +249,9 @@ static void mgw_fsm_assign_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state } LOGPFSML(fi, LOGL_DEBUG, "forwarding modified RAB-AssignmentRequest to HNB\n"); - rua_tx_dt(map->hnb_ctx, map->is_ps, map->rua_ctx_id, msg->data, msg->len); - msgb_free(msg); + msg->l2h = msg->data; + talloc_steal(OTC_SELECT, msg); + map_rua_dispatch(map, MAP_RUA_EV_TX_DIRECT_TRANSFER, msg); } static void mgw_fsm_assign(struct osmo_fsm_inst *fi, uint32_t event, void *data) @@ -274,8 +269,6 @@ static void mgw_fsm_mdcx_hnb_onenter(struct osmo_fsm_inst *fi, uint32_t prev_sta { struct mgw_fsm_priv *mgw_fsm_priv = fi->priv; struct hnbgw_context_map *map = mgw_fsm_priv->map; - struct hnb_context *hnb = map->hnb_ctx; - struct hnbgw_cnlink *cn = hnb->gw->sccp.cnlink; struct mgcp_conn_peer mgw_info; struct osmo_sockaddr addr; struct osmo_sockaddr_str addr_str; @@ -298,6 +291,8 @@ static void mgw_fsm_mdcx_hnb_onenter(struct osmo_fsm_inst *fi, uint32_t prev_sta if (rc < 0) { rab_failed_at_hnb = ranap_rab_ass_resp_ies_check_failure(ies, mgw_fsm_priv->rab_id); if (rab_failed_at_hnb) { + struct msgb *msg; + LOGPFSML(fi, LOGL_ERROR, "The RAB-AssignmentResponse contains a RAB-FailedList, RAB-Assignment (%u) failed.\n", mgw_fsm_priv->rab_id); @@ -305,8 +300,12 @@ static void mgw_fsm_mdcx_hnb_onenter(struct osmo_fsm_inst *fi, uint32_t prev_sta /* Forward the RAB-AssignmentResponse transparently. This will ensure that the MSC is informed * about the problem. */ LOGPFSML(fi, LOGL_DEBUG, "forwarding unmodified RAB-AssignmentResponse to MSC\n"); - rc = osmo_sccp_user_sap_down(cn->sccp_user, mgw_fsm_priv->ranap_rab_ass_resp_oph); - mgw_fsm_priv->ranap_rab_ass_resp_oph = NULL; + + msg = mgw_fsm_priv->ranap_rab_ass_resp_msgb; + mgw_fsm_priv->ranap_rab_ass_resp_msgb = NULL; + talloc_steal(OTC_SELECT, msg); + + rc = map_sccp_dispatch(map, MAP_SCCP_EV_TX_DATA_REQUEST, msg); if (rc < 0) { LOGPFSML(fi, LOGL_DEBUG, "failed to forward RAB-AssignmentResponse message\n"); osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0); @@ -431,15 +430,15 @@ static void mgw_fsm_crcx_msc(struct osmo_fsm_inst *fi, uint32_t event, void *dat * the original message. Ensure that there is enough room in l2h to grow. (The current implementation * should yield a message with the same size, but there is no guarantee for that) */ msg_max_len = - msgb_l2len(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg) + - msgb_tailroom(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg); - rc = msgb_resize_area(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg, - mgw_fsm_priv->ranap_rab_ass_resp_oph->msg->l2h, - msgb_l2len(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg), msg_max_len); + msgb_l2len(mgw_fsm_priv->ranap_rab_ass_resp_msgb) + + msgb_tailroom(mgw_fsm_priv->ranap_rab_ass_resp_msgb); + rc = msgb_resize_area(mgw_fsm_priv->ranap_rab_ass_resp_msgb, + mgw_fsm_priv->ranap_rab_ass_resp_msgb->l2h, + msgb_l2len(mgw_fsm_priv->ranap_rab_ass_resp_msgb), msg_max_len); OSMO_ASSERT(rc == 0); - rc = ranap_rab_ass_resp_encode(msgb_l2(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg), - msgb_l2len(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg), ies); + rc = ranap_rab_ass_resp_encode(msgb_l2(mgw_fsm_priv->ranap_rab_ass_resp_msgb), + msgb_l2len(mgw_fsm_priv->ranap_rab_ass_resp_msgb), ies); if (rc < 0) { LOGPFSML(fi, LOGL_ERROR, "failed to re-encode RAB-AssignmentResponse message\n"); osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0); @@ -447,9 +446,9 @@ static void mgw_fsm_crcx_msc(struct osmo_fsm_inst *fi, uint32_t event, void *dat } /* Resize l2h back to the actual message length */ - rc = msgb_resize_area(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg, - mgw_fsm_priv->ranap_rab_ass_resp_oph->msg->l2h, - msgb_l2len(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg), rc); + rc = msgb_resize_area(mgw_fsm_priv->ranap_rab_ass_resp_msgb, + mgw_fsm_priv->ranap_rab_ass_resp_msgb->l2h, + msgb_l2len(mgw_fsm_priv->ranap_rab_ass_resp_msgb), rc); OSMO_ASSERT(rc == 0); /* When the established state is entered, the modified RAB AssignmentResponse is forwarded to the MSC. @@ -466,14 +465,16 @@ static void mgw_fsm_established_onenter(struct osmo_fsm_inst *fi, uint32_t prev_ { struct mgw_fsm_priv *mgw_fsm_priv = fi->priv; struct hnbgw_context_map *map = mgw_fsm_priv->map; - struct osmo_prim_hdr *oph = mgw_fsm_priv->ranap_rab_ass_resp_oph; - struct hnb_context *hnb = map->hnb_ctx; - struct hnbgw_cnlink *cn = hnb->gw->sccp.cnlink; + struct msgb *ranap_msg; int rc; LOGPFSML(fi, LOGL_DEBUG, "forwarding modified RAB-AssignmentResponse to MSC\n"); - rc = osmo_sccp_user_sap_down(cn->sccp_user, oph); - mgw_fsm_priv->ranap_rab_ass_resp_oph = NULL; + + ranap_msg = mgw_fsm_priv->ranap_rab_ass_resp_msgb; + mgw_fsm_priv->ranap_rab_ass_resp_msgb = NULL; + talloc_steal(OTC_SELECT, ranap_msg); + + rc = map_sccp_dispatch(map, MAP_SCCP_EV_TX_DATA_REQUEST, ranap_msg); if (rc < 0) { LOGPFSML(fi, LOGL_DEBUG, "failed to forward RAB-AssignmentResponse message\n"); osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0); @@ -530,28 +531,6 @@ static int mgw_fsm_timer_cb(struct osmo_fsm_inst *fi) static void mgw_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause) { struct mgw_fsm_priv *mgw_fsm_priv = fi->priv; - struct osmo_scu_prim *scu_prim; - struct msgb *scu_msg; - - if (mgw_fsm_priv->ranap_rab_ass_req_message) { - ranap_ran_rx_co_free(mgw_fsm_priv->ranap_rab_ass_req_message); - talloc_free(mgw_fsm_priv->ranap_rab_ass_req_message); - mgw_fsm_priv->ranap_rab_ass_req_message = NULL; - } - - if (mgw_fsm_priv->ranap_rab_ass_resp_message) { - ranap_cn_rx_co_free(mgw_fsm_priv->ranap_rab_ass_resp_message); - talloc_free(mgw_fsm_priv->ranap_rab_ass_resp_message); - mgw_fsm_priv->ranap_rab_ass_resp_message = NULL; - } - - if (mgw_fsm_priv->ranap_rab_ass_resp_oph) { - scu_prim = (struct osmo_scu_prim *)mgw_fsm_priv->ranap_rab_ass_resp_oph; - scu_msg = scu_prim->oph.msg; - msgb_free(scu_msg); - mgw_fsm_priv->ranap_rab_ass_resp_oph = NULL; - } - talloc_free(mgw_fsm_priv); } @@ -654,7 +633,7 @@ static struct osmo_fsm mgw_fsm = { }; /* The MSC may ask to release a specific RAB within a RAB-AssignmentRequest */ -static int handle_rab_release(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message) +static int handle_rab_release(struct hnbgw_context_map *map, struct msgb *ranap_msg, ranap_message *message) { bool rab_release_req; struct osmo_fsm_inst *fi = map->mgw_fi; @@ -672,7 +651,7 @@ static int handle_rab_release(struct hnbgw_context_map *map, struct osmo_prim_hd /* Forward the unmodifed RAB-AssignmentRequest to HNB, so that the HNB is informed about the RAB release as * well */ LOGPFSML(fi, LOGL_DEBUG, "forwarding unmodified RAB-AssignmentRequest to HNB\n"); - rc = rua_tx_dt(map->hnb_ctx, map->is_ps, map->rua_ctx_id, msgb_l2(oph->msg), msgb_l2len(oph->msg)); + rc = map_rua_dispatch(map, MAP_RUA_EV_TX_DIRECT_TRANSFER, ranap_msg); /* Release the FSM normally */ osmo_fsm_inst_state_chg(fi, MGW_ST_RELEASE, 0, 0); @@ -680,12 +659,14 @@ static int handle_rab_release(struct hnbgw_context_map *map, struct osmo_prim_hd return rc; } -/*! Allocate MGW FSM and handle RANAP RAB AssignmentRequest). - * \ptmap[in] map hanbgw context map that is responsible for this call. - * \ptmap[in] oph osmo prim header with RANAP RAB AssignmentResponse (function takes no ownership). - * \ptmap[in] message ranap message container (function takes ownership). +/*! Allocate MGW FSM and handle RANAP RAB AssignmentRequest. + * \param[in] map hnbgw context map that is responsible for this call. + * \param[in] ranap_msg msgb containing RANAP RAB AssignmentRequest at msgb_l2(), allocated in OTC_SELECT. + * This function may talloc_steal(ranap_msg) to keep it for later. + * \param[in] message decoded RANAP message container, allocated in OTC_SELECT. + * This function may talloc_steal(message) to keep it for later. * \returns 0 on success; negative on error. */ -int handle_rab_ass_req(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message) +int handle_rab_ass_req(struct hnbgw_context_map *map, struct msgb *ranap_msg, ranap_message *message) { static bool initialized = false; struct mgw_fsm_priv *mgw_fsm_priv; @@ -703,7 +684,7 @@ int handle_rab_ass_req(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, * a ReleaseList. In this case an FSM will already be present. */ if (map->mgw_fi) { /* A RAB Release might be in progress, handle it */ - rc = handle_rab_release(map, oph, message); + rc = handle_rab_release(map, ranap_msg, message); if (rc >= 0) return rc; @@ -726,6 +707,8 @@ int handle_rab_ass_req(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, mgw_fsm_priv = talloc_zero(map, struct mgw_fsm_priv); mgw_fsm_priv->map = map; + + talloc_steal(mgw_fsm_priv, message); mgw_fsm_priv->ranap_rab_ass_req_message = message; /* Allocate FSM */ @@ -738,17 +721,17 @@ int handle_rab_ass_req(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, } /*! Handlie RANAP RAB AssignmentResponse (deliver message, complete RTP stream switching). - * \ptmap[in] map hanbgw context map that is responsible for this call. - * \ptmap[in] oph osmo prim header with RANAP RAB AssignmentResponse (function takes ownership). - * \ptmap[in] message ranap message container with decoded ranap message (function takes ownership). + * \param[in] map hnbgw context map that is responsible for this call. + * \param[in] ranap_msg msgb containing RANAP RAB AssignmentResponse at msgb_l2(), allocated in OTC_SELECT. + * This function may talloc_steal(ranap_msg) to keep it for later. + * \param[in] message decoded RANAP message container, allocated in OTC_SELECT. + * This function may talloc_steal(message) to keep it for later. * \returns 0 on success; negative on error. */ -int mgw_fsm_handle_rab_ass_resp(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message) +int mgw_fsm_handle_rab_ass_resp(struct hnbgw_context_map *map, struct msgb *ranap_msg, ranap_message *message) { struct mgw_fsm_priv *mgw_fsm_priv; - struct osmo_scu_prim *prim; - struct msgb *msg; - OSMO_ASSERT(oph); + OSMO_ASSERT(ranap_msg); if (!map->mgw_fi) { /* NOTE: This situation is a corner-case. We may end up here when the co-located MGW caused a problem @@ -758,23 +741,19 @@ int mgw_fsm_handle_rab_ass_resp(struct hnbgw_context_map *map, struct osmo_prim_ "mgw_fsm_handle_rab_ass_resp() rua_ctx_id=%d, no MGW fsm -- sending Iu-Release-Request!\n", map->rua_ctx_id); - /* Cleanup ranap message */ - ranap_cn_rx_co_free(message); - talloc_free(message); - - /* Toss RAB-AssignmentResponse */ - prim = (struct osmo_scu_prim *)oph; - msg = prim->oph.msg; - msgb_free(msg); - /* Send a release request, to make sure that the MSC is aware of the problem. */ tx_release_req(map); return -1; } mgw_fsm_priv = map->mgw_fi->priv; - mgw_fsm_priv->ranap_rab_ass_resp_oph = oph; + + talloc_steal(mgw_fsm_priv, ranap_msg); + mgw_fsm_priv->ranap_rab_ass_resp_msgb = ranap_msg; + + talloc_steal(mgw_fsm_priv, message); mgw_fsm_priv->ranap_rab_ass_resp_message = message; + osmo_fsm_inst_dispatch(map->mgw_fi, MGW_EV_RAB_ASS_RESP, NULL); return 0; } diff --git a/src/osmo-hnbgw/ps_rab_ass_fsm.c b/src/osmo-hnbgw/ps_rab_ass_fsm.c index e232de8..65a6116 100644 --- a/src/osmo-hnbgw/ps_rab_ass_fsm.c +++ b/src/osmo-hnbgw/ps_rab_ass_fsm.c @@ -110,7 +110,7 @@ struct ps_rab_ass { ranap_message *ranap_rab_ass_req_message; ranap_message *ranap_rab_ass_resp_message; - struct osmo_prim_hdr *ranap_rab_ass_resp_oph; + struct msgb *ranap_rab_ass_resp_msgb; /* A RAB Assignment may contain more than one RAB. Each RAB sets up a distinct ps_rab_fsm (aka PFCP session) and * reports back about local F-TEIDs assigned by the UPF. This gives the nr of RAB events we expect from @@ -214,16 +214,25 @@ error_exit: return rc; } -int hnbgw_gtpmap_rx_rab_ass_req(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message) +/*! Allocate ps_rab_ass_fsm and handle PS RANAP RAB AssignmentRequest. + * \param[in] map hnbgw context map that is responsible for this conn. + * \param[in] ranap_msg msgb containing RANAP RAB AssignmentRequest at msgb_l2(), allocated in OTC_SELECT. + * This function may talloc_steal(ranap_msg) to keep it for later. + * \param[in] message decoded RANAP message container, allocated in OTC_SELECT. + * This function may talloc_steal(message) to keep it for later. + * \returns 0 on success; negative on error. */ +int hnbgw_gtpmap_rx_rab_ass_req(struct hnbgw_context_map *map, struct msgb *ranap_msg, ranap_message *message) { RANAP_RAB_AssignmentRequestIEs_t *ies = &message->msg.raB_AssignmentRequestIEs; int i; - struct hnb_gw *hnb_gw = map->hnb_ctx->gw; + struct hnb_gw *hnb_gw = map->gw; struct ps_rab_ass *rab_ass; struct osmo_fsm_inst *fi; rab_ass = ps_rab_ass_alloc(map); + + talloc_steal(rab_ass, message); rab_ass->ranap_rab_ass_req_message = message; /* Now rab_ass owns message and will clean it up */ @@ -351,8 +360,10 @@ continue_cleanloop: ps_rab_ass_failure(rab_ass); return; } - rua_tx_dt(rab_ass->map->hnb_ctx, rab_ass->map->is_ps, rab_ass->map->rua_ctx_id, msg->data, msg->len); - msgb_free(msg); + talloc_steal(OTC_SELECT, msg); + msg->l2h = msg->data; + map_rua_dispatch(rab->map, MAP_RUA_EV_TX_DIRECT_TRANSFER, msg); + /* The request message has been forwarded. The response will be handled by a new FSM instance. * We are done. */ osmo_fsm_inst_term(rab_ass->fi, OSMO_FSM_TERM_REGULAR, NULL); @@ -385,7 +396,7 @@ static int ps_rab_setup_access_remote(struct ps_rab_ass *rab_ass, return ps_rab_rx_access_remote_f_teid(map, rab_id, &args); } -int hnbgw_gtpmap_rx_rab_ass_resp(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message) +int hnbgw_gtpmap_rx_rab_ass_resp(struct hnbgw_context_map *map, struct msgb *ranap_msg, ranap_message *message) { /* hNodeB responds with its own F-TEIDs. Need to tell the UPF about those to complete the GTP mapping. * 1. here, extract the F-TEIDs (one per RAB), @@ -408,7 +419,7 @@ int hnbgw_gtpmap_rx_rab_ass_resp(struct hnbgw_context_map *map, struct osmo_prim struct ps_rab_ass *rab_ass; struct osmo_fsm_inst *fi; RANAP_RAB_AssignmentResponseIEs_t *ies; - struct hnb_gw *hnb_gw = map->hnb_ctx->gw; + struct hnb_gw *hnb_gw = map->gw; /* Make sure we indeed deal with a setup-or-modify list */ ies = &message->msg.raB_AssignmentResponseIEs; @@ -418,8 +429,12 @@ int hnbgw_gtpmap_rx_rab_ass_resp(struct hnbgw_context_map *map, struct osmo_prim } rab_ass = ps_rab_ass_alloc(map); + + talloc_steal(rab_ass, message); rab_ass->ranap_rab_ass_resp_message = message; - rab_ass->ranap_rab_ass_resp_oph = oph; + + talloc_steal(rab_ass, ranap_msg); + rab_ass->ranap_rab_ass_resp_msgb = ranap_msg; /* Now rab_ass owns message and will clean it up */ if (!osmo_pfcp_cp_peer_is_associated(hnb_gw->pfcp.cp_peer)) { @@ -494,8 +509,8 @@ static void ps_rab_ass_resp_send_if_ready(struct ps_rab_ass *rab_ass) { int i; int rc; - struct hnbgw_cnlink *cn = rab_ass->map->cn_link; RANAP_RAB_AssignmentResponseIEs_t *ies = &rab_ass->ranap_rab_ass_resp_message->msg.raB_AssignmentResponseIEs; + struct msgb *msg; /* Go through all RABs in the RAB Assignment Response message and replace with the F-TEID that the UPF assigned, * verifying that instructing the UPF has succeeded. */ @@ -565,10 +580,13 @@ continue_cleanloop: ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifiedItem, &item_ies); } + msg = rab_ass->ranap_rab_ass_resp_msgb; + rab_ass->ranap_rab_ass_resp_msgb = NULL; + talloc_steal(OTC_SELECT, msg); + /* Replaced all the GTP info, re-encode the message. Since we are replacing data 1:1, taking care to use the * same IP address encoding, the resulting message size must be identical to the original message size. */ - rc = ranap_rab_ass_resp_encode(msgb_l2(rab_ass->ranap_rab_ass_resp_oph->msg), - msgb_l2len(rab_ass->ranap_rab_ass_resp_oph->msg), ies); + rc = ranap_rab_ass_resp_encode(msgb_l2(msg), msgb_l2len(msg), ies); if (rc < 0) { LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Re-encoding RANAP PS RAB-AssignmentResponse failed\n"); ps_rab_ass_failure(rab_ass); @@ -576,8 +594,8 @@ continue_cleanloop: } LOG_PS_RAB_ASS(rab_ass, LOGL_NOTICE, "Sending RANAP PS RAB-AssignmentResponse with mapped GTP info\n"); - rc = osmo_sccp_user_sap_down(cn->sccp_user, rab_ass->ranap_rab_ass_resp_oph); - rab_ass->ranap_rab_ass_resp_oph = NULL; + + rc = map_sccp_dispatch(rab_ass->map, MAP_SCCP_EV_TX_DATA_REQUEST, msg); if (rc < 0) { LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Sending RANAP PS RAB-AssignmentResponse failed\n"); ps_rab_ass_failure(rab_ass); @@ -590,29 +608,8 @@ continue_cleanloop: static void ps_rab_ass_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause) { struct ps_rab_ass *rab_ass = fi->priv; - struct osmo_scu_prim *scu_prim; - struct msgb *scu_msg; struct ps_rab *rab; - if (rab_ass->ranap_rab_ass_req_message) { - ranap_ran_rx_co_free(rab_ass->ranap_rab_ass_req_message); - talloc_free(rab_ass->ranap_rab_ass_req_message); - rab_ass->ranap_rab_ass_req_message = NULL; - } - - if (rab_ass->ranap_rab_ass_resp_message) { - ranap_cn_rx_co_free(rab_ass->ranap_rab_ass_resp_message); - talloc_free(rab_ass->ranap_rab_ass_resp_message); - rab_ass->ranap_rab_ass_resp_message = NULL; - } - - if (rab_ass->ranap_rab_ass_resp_oph) { - scu_prim = (struct osmo_scu_prim *)rab_ass->ranap_rab_ass_resp_oph; - scu_msg = scu_prim->oph.msg; - msgb_free(scu_msg); - rab_ass->ranap_rab_ass_resp_oph = NULL; - } - llist_for_each_entry(rab, &rab_ass->map->ps_rabs, entry) { if (rab->req_fi == fi) rab->req_fi = NULL; diff --git a/src/osmo-hnbgw/ps_rab_fsm.c b/src/osmo-hnbgw/ps_rab_fsm.c index dcc18ca..f4a1f12 100644 --- a/src/osmo-hnbgw/ps_rab_fsm.c +++ b/src/osmo-hnbgw/ps_rab_fsm.c @@ -87,7 +87,7 @@ static struct ps_rab *ps_rab_alloc(struct hnbgw_context_map *map, uint8_t rab_id /* Allocate with the global hnb_gw, so that we can gracefully handle PFCP release even if a hnb_ctx gets * deallocated. */ - fi = osmo_fsm_inst_alloc(&ps_rab_fsm, map->hnb_ctx->gw, NULL, LOGL_DEBUG, NULL); + fi = osmo_fsm_inst_alloc(&ps_rab_fsm, map->gw, NULL, LOGL_DEBUG, NULL); OSMO_ASSERT(fi); osmo_fsm_inst_update_id_f_sanitize(fi, '-', "%s-RUA-%u-RAB-%u", hnb_context_name(map->hnb_ctx), map->rua_ctx_id, rab_id); @@ -96,7 +96,7 @@ static struct ps_rab *ps_rab_alloc(struct hnbgw_context_map *map, uint8_t rab_id OSMO_ASSERT(rab); *rab = (struct ps_rab){ .fi = fi, - .hnb_gw = map->hnb_ctx->gw, + .hnb_gw = map->gw, .map = map, .rab_id = rab_id, .use_count = { @@ -683,6 +683,7 @@ static void ps_rab_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, static void ps_rab_forget_map(struct ps_rab *rab) { + /* remove from map->ps_rabs */ if (rab->map) llist_del(&rab->entry); rab->map = NULL; diff --git a/src/osmo-hnbgw/tdefs.c b/src/osmo-hnbgw/tdefs.c index d798345..7af292d 100644 --- a/src/osmo-hnbgw/tdefs.c +++ b/src/osmo-hnbgw/tdefs.c @@ -36,9 +36,15 @@ struct osmo_tdef ps_T_defs[] = { { } }; +struct osmo_tdef cmap_T_defs[] = { + {.T = -31, .default_val = 5, .desc = "Timeout for discarding a partially released context map (RUA <-> SCCP)" }, + { } +}; + struct osmo_tdef_group hnbgw_tdef_group[] = { {.name = "mgw", .tdefs = mgw_fsm_T_defs, .desc = "MGW (Media Gateway) interface" }, {.name = "ps", .tdefs = ps_T_defs, .desc = "timers for Packet Switched domain" }, + {.name = "cmap", .tdefs = cmap_T_defs, .desc = "timers for context maps (RUA <-> SCCP)" }, #if ENABLE_PFCP {.name = "pfcp", .tdefs = osmo_pfcp_tdefs, .desc = "PFCP timers" }, #endif