diff --git a/include/osmocom/bsc_nat/bssap.h b/include/osmocom/bsc_nat/bssap.h index 996cb69..ca387c2 100644 --- a/include/osmocom/bsc_nat/bssap.h +++ b/include/osmocom/bsc_nat/bssap.h @@ -23,3 +23,5 @@ int bssap_handle_udt(struct bsc_nat_sccp_inst *sccp_inst, struct osmo_sccp_addr *addr, struct msgb *msgb, unsigned int length); + +int bssap_tx_reset(struct bsc_nat_sccp_inst *sccp_inst, struct osmo_sccp_addr *addr); diff --git a/include/osmocom/bsc_nat/msc.h b/include/osmocom/bsc_nat/msc.h index 1f91de0..47b4d13 100644 --- a/include/osmocom/bsc_nat/msc.h +++ b/include/osmocom/bsc_nat/msc.h @@ -19,11 +19,13 @@ #pragma once +#include #include struct msc { struct llist_head list; struct osmo_sccp_addr addr; + struct osmo_fsm_inst *fi; }; struct msc *msc_alloc(struct osmo_sccp_addr *addr); @@ -31,4 +33,7 @@ int msc_alloc_from_addr_book(void); struct msc *msc_get(void); +void msc_tx_reset(struct msc *msc); +void msc_rx_reset_ack(struct msc *msc); + void msc_free(struct msc *msc); diff --git a/src/osmo-bsc-nat/Makefile.am b/src/osmo-bsc-nat/Makefile.am index 1dd82fd..8fd5667 100644 --- a/src/osmo-bsc-nat/Makefile.am +++ b/src/osmo-bsc-nat/Makefile.am @@ -32,6 +32,7 @@ osmo_bsc_nat_SOURCES = \ logging.c \ main.c \ msc.c \ + msc_fsm.c \ vty.c \ $(NULL) diff --git a/src/osmo-bsc-nat/bsc_nat_fsm.c b/src/osmo-bsc-nat/bsc_nat_fsm.c index 4193ae9..1d92ba3 100644 --- a/src/osmo-bsc-nat/bsc_nat_fsm.c +++ b/src/osmo-bsc-nat/bsc_nat_fsm.c @@ -286,24 +286,6 @@ static int sccp_sap_up_ran(struct osmo_prim_hdr *oph, void *scu) case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION): /* connection-less data received */ rc = bssap_handle_udt(sccp_inst, &prim->u.unitdata.calling_addr, oph->msg, msgb_l2len(oph->msg)); - - /* FIXME: don't forward this to the MSC anymore, as soon as the - * BSCNAT is able to do the RESET to MSC by itself. */ - addr = &prim->u.unitdata.calling_addr; - - if (sccp_sap_get_peer_addr_out(sccp_inst, addr, &peer_addr_out) < 0) - goto error; - - LOGP(DMAIN, LOGL_DEBUG, "Fwd to %s\n", bsc_nat_print_addr_cn(&peer_addr_out)); - - /* oph->msg stores oph and unitdata msg. Move oph->msg->data to - * unitdata msg and send it again. */ - msgb_pull_to_l2(oph->msg); - osmo_sccp_tx_unitdata(g_bsc_nat->cn.sccp_inst->scu, - &g_bsc_nat->cn.sccp_inst->addr, - &peer_addr_out, - oph->msg->data, - msgb_length(oph->msg)); break; default: diff --git a/src/osmo-bsc-nat/bssap.c b/src/osmo-bsc-nat/bssap.c index 83eec99..f88bb20 100644 --- a/src/osmo-bsc-nat/bssap.c +++ b/src/osmo-bsc-nat/bssap.c @@ -25,6 +25,50 @@ #include #include #include +#include + +int bssap_tx_reset(struct bsc_nat_sccp_inst *sccp_inst, struct osmo_sccp_addr *addr) +{ + enum bsc_nat_net net = sccp_inst == g_bsc_nat->cn.sccp_inst ? BSC_NAT_NET_CN : BSC_NAT_NET_RAN; + + LOGP(DMAIN, LOGL_NOTICE, "Tx RESET to %s\n", bsc_nat_print_addr(net, addr)); + + struct msgb *msg = gsm0808_create_reset(); + + return osmo_sccp_tx_unitdata_msg(sccp_inst->scu, &sccp_inst->addr, addr, msg); +} + +static int bssap_cn_handle_reset_ack(struct osmo_sccp_addr *addr, struct msgb *msg, unsigned int length) +{ + struct msc *msc = msc_get(); + + if (msc->addr.pc != addr->pc) { + LOGP(DMAIN, LOGL_ERROR, "Unexpected Rx RESET ACK in CN from %s, which is not %s\n", + osmo_ss7_pointcode_print(NULL, addr->pc), talloc_get_name(msc)); + return -1; + } + + LOGP(DMAIN, LOGL_NOTICE, "Rx RESET ACK from %s\n", talloc_get_name(msc)); + msc_rx_reset_ack(msc); + + return 0; +} + +static int bssap_cn_rcvmsg_udt(struct osmo_sccp_addr *addr, struct msgb *msg, unsigned int length) +{ + int ret = 0; + + switch (msg->l3h[0]) { + case BSS_MAP_MSG_RESET_ACKNOWLEDGE: + ret = bssap_cn_handle_reset_ack(addr, msg, length); + break; + default: + LOGP(DMAIN, LOGL_NOTICE, "Unimplemented BSSMAP UDT %s\n", gsm0808_bssap_name(msg->l3h[0])); + break; + } + + return ret; +} static int bssap_ran_handle_reset(struct osmo_sccp_addr *addr, struct msgb *msg, unsigned int length) { @@ -69,7 +113,8 @@ static int bssap_rcvmsg_udt(struct bsc_nat_sccp_inst *sccp_inst, struct osmo_scc LOGP(DMAIN, LOGL_NOTICE, "Rx UDT BSSMAP %s\n", gsm0808_bssap_name(msg->l3h[0])); - /* NOTE: bssap_cn_rcvmsg_udt() will be added in a future patch */ + if (sccp_inst == g_bsc_nat->cn.sccp_inst) + return bssap_cn_rcvmsg_udt(addr, msg, length); return bssap_ran_rcvmsg_udt(addr, msg, length); } diff --git a/src/osmo-bsc-nat/msc.c b/src/osmo-bsc-nat/msc.c index 70f9d76..e78a8e6 100644 --- a/src/osmo-bsc-nat/msc.c +++ b/src/osmo-bsc-nat/msc.c @@ -23,6 +23,8 @@ #include #include +extern struct osmo_fsm msc_fsm; + struct msc *msc_alloc(struct osmo_sccp_addr *addr) { struct msc *msc = talloc_zero(g_bsc_nat, struct msc); @@ -37,6 +39,11 @@ struct msc *msc_alloc(struct osmo_sccp_addr *addr) INIT_LLIST_HEAD(&msc->list); llist_add(&msc->list, &g_bsc_nat->cn.mscs); + msc->fi = osmo_fsm_inst_alloc(&msc_fsm, msc, msc, LOGL_INFO, NULL); + OSMO_ASSERT(msc->fi); + + msc_tx_reset(msc); + return msc; } @@ -67,5 +74,6 @@ void msc_free(struct msc *msc) { LOGP(DMAIN, LOGL_DEBUG, "Del %s\n", talloc_get_name(msc)); llist_del(&msc->list); + osmo_fsm_inst_free(msc->fi); talloc_free(msc); } diff --git a/src/osmo-bsc-nat/msc_fsm.c b/src/osmo-bsc-nat/msc_fsm.c new file mode 100644 index 0000000..d8b89c6 --- /dev/null +++ b/src/osmo-bsc-nat/msc_fsm.c @@ -0,0 +1,154 @@ +/* (C) 2022 by sysmocom - s.f.m.c. GmbH + * Author: Oliver Smith + * All Rights Reserved + * + * 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 "config.h" +#include +#include +#include +#include +#include +#include + +#define X(s) (1 << (s)) + +enum msc_fsm_states { + MSC_FSM_ST_DISCONNECTED, + MSC_FSM_ST_CONNECTING, + MSC_FSM_ST_CONNECTED, +}; + +enum msc_fsm_events { + MSC_FSM_EV_TX_RESET, + MSC_FSM_EV_RX_RESET_ACK, + MSC_FSM_EV_DISCONNECT +}; + +static void st_connecting(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case MSC_FSM_EV_RX_RESET_ACK: + osmo_fsm_inst_state_chg(fi, MSC_FSM_ST_CONNECTED, 0, 0); + break; + default: + OSMO_ASSERT(false); + } +} + +static void st_disconnected_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_stat) +{ + osmo_fsm_inst_dispatch(fi, MSC_FSM_EV_TX_RESET, NULL); +} + +static void st_disconnected(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct msc *msc = fi->priv; + + switch (event) { + case MSC_FSM_EV_TX_RESET: + LOGP(DMAIN, LOGL_DEBUG, "Tx RESET to %s\n", talloc_get_name(msc)); + + if (bssap_tx_reset(g_bsc_nat->cn.sccp_inst, &msc->addr) < 0) { + LOGP(DMAIN, LOGL_ERROR, "Could not send RESET to MSC (SCCP not up yet?)\n"); + } + + /* Retry in 3s if RESET ACK was not received from MSC */ + osmo_fsm_inst_state_chg(fi, MSC_FSM_ST_CONNECTING, 3, 0); + + break; + default: + OSMO_ASSERT(false); + } +} + +int msc_fsm_timer_cb(struct osmo_fsm_inst *fi) +{ + switch (fi->state) { + case MSC_FSM_ST_CONNECTING: + osmo_fsm_inst_state_chg(fi, MSC_FSM_ST_DISCONNECTED, 0, 0); + break; + default: + OSMO_ASSERT(false); + } + return 0; +} + +static struct osmo_fsm_state msc_fsm_states[] = { + [MSC_FSM_ST_DISCONNECTED] = { + .name = "DISCONNECTED", + .in_event_mask = 0 + | X(MSC_FSM_EV_TX_RESET) + , + .out_state_mask = 0 + | X(MSC_FSM_ST_CONNECTING) + , + .action = st_disconnected, + .onenter = st_disconnected_on_enter, + }, + [MSC_FSM_ST_CONNECTING] = { + .name = "CONNECTING", + .in_event_mask = 0 + | X(MSC_FSM_EV_RX_RESET_ACK) + , + .out_state_mask = 0 + | X(MSC_FSM_ST_CONNECTED) + | X(MSC_FSM_ST_DISCONNECTED) + , + .action = st_connecting, + }, + [MSC_FSM_ST_CONNECTED] = { + .name = "CONNECTED", + .in_event_mask = 0 + | X(MSC_FSM_EV_DISCONNECT) + , + .out_state_mask = 0 + | X(MSC_FSM_ST_DISCONNECTED) + , + }, +}; + +const struct value_string msc_fsm_event_names[] = { + OSMO_VALUE_STRING(MSC_FSM_EV_TX_RESET), + OSMO_VALUE_STRING(MSC_FSM_EV_RX_RESET_ACK), + OSMO_VALUE_STRING(MSC_FSM_EV_DISCONNECT), + { 0, NULL } +}; + +struct osmo_fsm msc_fsm = { + .name = "MSC", + .states = msc_fsm_states, + .num_states = ARRAY_SIZE(msc_fsm_states), + .log_subsys = DMAIN, + .event_names = msc_fsm_event_names, + .timer_cb = msc_fsm_timer_cb, +}; + +static __attribute__((constructor)) void msc_fsm_init(void) +{ + OSMO_ASSERT(osmo_fsm_register(&msc_fsm) == 0); +} + +void msc_tx_reset(struct msc *msc) +{ + osmo_fsm_inst_dispatch(msc->fi, MSC_FSM_EV_TX_RESET, NULL); +} + +void msc_rx_reset_ack(struct msc *msc) +{ + osmo_fsm_inst_dispatch(msc->fi, MSC_FSM_EV_RX_RESET_ACK, NULL); +}