diff --git a/TODO-RELEASE b/TODO-RELEASE index 285dc63a3..98023fc3a 100644 --- a/TODO-RELEASE +++ b/TODO-RELEASE @@ -12,3 +12,4 @@ osmo-bsc CTRL,VTY osmo_fsm instance IDs now use new dynamic timeslot names 'DYNAMIC_OSMOCOM' and 'DYNAMIC_IPACCESS' libosmogsm >1.8.0 circuit switched data stuff (gsm0808_enc/dec_channel_type etc.) libosmo-abis >1.4.0 osmo_ortp.h: add RTP_PT_CSDATA +libosmo-sccp >1.7.0 osmo_sccp_{get,set}_priv() \ No newline at end of file diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h index cdf0f4322..0c5b485a3 100644 --- a/include/osmocom/bsc/gsm_data.h +++ b/include/osmocom/bsc/gsm_data.h @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include @@ -331,6 +333,9 @@ struct gsm_subscriber_connection { /* SCCP connection related */ struct bsc_msc_data *msc; + /* entry in (struct bsc_sccp_inst)->connections */ + struct rb_node node; + /* Sigtran connection ID: * if set: Range (0..SCCP_CONN_ID_MAX) (24 bit) * if unset: SCCP_CONN_ID_UNSET (-1) if unset */ @@ -871,6 +876,19 @@ struct all_allocated { struct osmo_time_cc static_tch; }; +struct bsc_sccp_inst { + struct osmo_sccp_instance *sccp; /* backpointer */ + /* rbtree root of 'sstruct gsm_subscriber_connection' in this instance, ordered by conn_id */ + struct rb_root connections; + uint32_t next_id; /* next id to allocate */ +}; + +struct bsc_sccp_inst *bsc_sccp_inst_alloc(void *ctx); +uint32_t bsc_sccp_inst_next_conn_id(struct bsc_sccp_inst *bsc_sccp); +int bsc_sccp_inst_register_gscon(struct bsc_sccp_inst *bsc_sccp, struct gsm_subscriber_connection *conn); +void bsc_sccp_inst_unregister_gscon(struct bsc_sccp_inst *bsc_sccp, struct gsm_subscriber_connection *conn); +struct gsm_subscriber_connection *bsc_sccp_inst_get_gscon_by_conn_id(const struct bsc_sccp_inst *bsc_sccp, uint32_t conn_id); + struct gsm_network { struct osmo_plmn_id plmn; @@ -1030,8 +1048,6 @@ enum gsm_phys_chan_config gsm_pchan_by_lchan_type(enum gsm_chan_t type); enum gsm48_rr_cause bsc_gsm48_rr_cause_from_gsm0808_cause(enum gsm0808_cause c); enum gsm48_rr_cause bsc_gsm48_rr_cause_from_rsl_cause(uint8_t c); -uint32_t bsc_sccp_inst_next_conn_id(struct osmo_sccp_instance *sccp); - /* Interference Measurement Parameters */ struct gsm_interf_meas_params { /* Intave: Interference Averaging period (see 3GPP TS 45.008, table A.1) */ diff --git a/src/osmo-bsc/bsc_sccp.c b/src/osmo-bsc/bsc_sccp.c index e0cce39bf..6fa1d4b4e 100644 --- a/src/osmo-bsc/bsc_sccp.c +++ b/src/osmo-bsc/bsc_sccp.c @@ -27,11 +27,82 @@ #include #include -/* We need an unused SCCP conn_id across all SCCP users. */ -uint32_t bsc_sccp_inst_next_conn_id(struct osmo_sccp_instance *sccp) +struct bsc_sccp_inst *bsc_sccp_inst_alloc(void *ctx) { - static uint32_t next_id = 1; - int i; + struct bsc_sccp_inst *bsc_sccp; + + bsc_sccp = talloc_zero(ctx, struct bsc_sccp_inst); + OSMO_ASSERT(bsc_sccp); + bsc_sccp->next_id = 1; + + return bsc_sccp; +} + +int bsc_sccp_inst_register_gscon(struct bsc_sccp_inst *bsc_sccp, struct gsm_subscriber_connection *conn) +{ + struct rb_node **n = &(bsc_sccp->connections.rb_node); + struct rb_node *parent = NULL; + uint32_t conn_id = conn->sccp.conn_id; + + OSMO_ASSERT(conn_id != SCCP_CONN_ID_UNSET); + + while (*n) { + struct gsm_subscriber_connection *it; + + it = container_of(*n, struct gsm_subscriber_connection, sccp.node); + + parent = *n; + if (conn_id < it->sccp.conn_id) { + n = &((*n)->rb_left); + } else if (conn_id > it->sccp.conn_id) { + n = &((*n)->rb_right); + } else { + LOGP(DMSC, LOGL_ERROR, + "Trying to reserve already reserved conn_id %u\n", conn_id); + return -EEXIST; + } + } + + rb_link_node(&conn->sccp.node, parent, n); + rb_insert_color(&conn->sccp.node, &bsc_sccp->connections); + return 0; +} + +void bsc_sccp_inst_unregister_gscon(struct bsc_sccp_inst *bsc_sccp, struct gsm_subscriber_connection *conn) +{ + OSMO_ASSERT(conn->sccp.conn_id != SCCP_CONN_ID_UNSET); + rb_erase(&conn->sccp.node, &bsc_sccp->connections); +} + +/* Helper function to Check if the given connection id is already assigned */ +struct gsm_subscriber_connection *bsc_sccp_inst_get_gscon_by_conn_id(const struct bsc_sccp_inst *bsc_sccp, uint32_t conn_id) +{ + const struct rb_node *node = bsc_sccp->connections.rb_node; + struct gsm_subscriber_connection *conn; + + OSMO_ASSERT(conn_id != SCCP_CONN_ID_UNSET); + /* Range (0..SCCP_CONN_ID_MAX) expected, see bsc_sccp_inst_next_conn_id() */ + OSMO_ASSERT(conn_id <= SCCP_CONN_ID_MAX); + + while (node) { + conn = container_of(node, struct gsm_subscriber_connection, sccp.node); + if (conn_id < conn->sccp.conn_id) + node = node->rb_left; + else if (conn_id > conn->sccp.conn_id) + node = node->rb_right; + else + return conn; + } + + return NULL; +} + +/* We need an unused SCCP conn_id across all SCCP users. */ +uint32_t bsc_sccp_inst_next_conn_id(struct bsc_sccp_inst *bsc_sccp) +{ + uint32_t first_id, test_id; + + first_id = test_id = bsc_sccp->next_id; /* SUA: RFC3868 sec 3.10.4: * The source reference number is a 4 octet long integer. @@ -41,59 +112,26 @@ uint32_t bsc_sccp_inst_next_conn_id(struct osmo_sccp_instance *sccp) * reference number which is generated and used by the local node to identify the * connection section after the connection section is set up. * The coding "all ones" is reserved for future use. - * Hence, let's simply use 24 bit ids to fit all link types (excluding 0x00ffffff). + *Hence, as we currently use the connection ID also as local reference, + *let's simply use 24 bit ids to fit all link types (excluding 0x00ffffff). */ - /* This looks really suboptimal, but in most cases the static next_id should indicate exactly the next unused - * conn_id, and we only iterate all conns once to make super sure that it is not already in use. */ - - /* SCCP towards SMLC: */ - if (bsc_gsmnet->smlc->sccp == sccp) { - for (i = 0; i < SCCP_CONN_ID_MAX; i++) { - struct gsm_subscriber_connection *conn; - uint32_t conn_id = next_id; - bool conn_id_already_used = false; - - /* Optimized modulo operation (% SCCP_CONN_ID_MAX) using bitwise AND plus CMP: */ - next_id = (next_id + 1) & 0x00FFFFFF; - if (OSMO_UNLIKELY(next_id == 0x00FFFFFF)) - next_id = 0; - - llist_for_each_entry(conn, &bsc_gsmnet->subscr_conns, entry) { - if (conn->lcs.lb.state != SUBSCR_SCCP_ST_NONE && - conn->lcs.lb.conn_id == conn_id) { - conn_id_already_used = true; - break; - } - } - - if (!conn_id_already_used) - return conn_id; - } - return 0xFFFFFFFF; - } - - /* SCCP towards MSC: */ - for (i = 0; i < SCCP_CONN_ID_MAX; i++) { - struct gsm_subscriber_connection *conn; - uint32_t conn_id = next_id; - bool conn_id_already_used = false; - + while (bsc_sccp_inst_get_gscon_by_conn_id(bsc_sccp, test_id)) { /* Optimized modulo operation (% SCCP_CONN_ID_MAX) using bitwise AND plus CMP: */ - next_id = (next_id + 1) & 0x00FFFFFF; - if (OSMO_UNLIKELY(next_id == 0x00FFFFFF)) - next_id = 0; + test_id = (test_id + 1) & 0x00FFFFFF; + if (OSMO_UNLIKELY(test_id == 0x00FFFFFF)) + test_id = 0; - llist_for_each_entry(conn, &bsc_gsmnet->subscr_conns, entry) { - if (conn->sccp.msc && conn->sccp.msc->a.sccp == sccp && - conn->sccp.conn_id == conn_id) { - conn_id_already_used = true; - break; - } - } - - if (!conn_id_already_used) - return conn_id; + /* Did a whole loop, all used, fail */ + if (OSMO_UNLIKELY(test_id == first_id)) + return SCCP_CONN_ID_UNSET; } - return SCCP_CONN_ID_UNSET; + + bsc_sccp->next_id = test_id; + /* Optimized modulo operation (% SCCP_CONN_ID_MAX) using bitwise AND plus CMP: */ + bsc_sccp->next_id = (bsc_sccp->next_id + 1) & 0x00FFFFFF; + if (OSMO_UNLIKELY(bsc_sccp->next_id == 0x00FFFFFF)) + bsc_sccp->next_id = 0; + + return test_id; } diff --git a/src/osmo-bsc/bsc_subscr_conn_fsm.c b/src/osmo-bsc/bsc_subscr_conn_fsm.c index e37f0300e..8e2199256 100644 --- a/src/osmo-bsc/bsc_subscr_conn_fsm.c +++ b/src/osmo-bsc/bsc_subscr_conn_fsm.c @@ -1111,6 +1111,11 @@ static void gscon_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cau osmo_sccp_tx_disconn(msc->a.sccp_user, conn->sccp.conn_id, &msc->a.bsc_addr, 0); conn->sccp.state = SUBSCR_SCCP_ST_NONE; } + if (conn->sccp.conn_id != SCCP_CONN_ID_UNSET && conn->sccp.msc) { + struct bsc_sccp_inst *bsc_sccp = osmo_sccp_get_priv(conn->sccp.msc->a.sccp); + bsc_sccp_inst_unregister_gscon(bsc_sccp, conn); + conn->sccp.conn_id = SCCP_CONN_ID_UNSET; + } if (conn->bsub) { LOGPFSML(fi, LOGL_DEBUG, "Putting bsc_subscr\n"); diff --git a/src/osmo-bsc/lb.c b/src/osmo-bsc/lb.c index 6e2d07498..6c6f599a8 100644 --- a/src/osmo-bsc/lb.c +++ b/src/osmo-bsc/lb.c @@ -31,22 +31,7 @@ #include #include #include - -static struct gsm_subscriber_connection *get_bsc_conn_by_lb_conn_id(uint32_t conn_id) -{ - struct gsm_subscriber_connection *conn; - - /* Range (0..SCCP_CONN_ID_MAX) expected, see bsc_sccp_inst_next_conn_id() */ - OSMO_ASSERT(conn_id <= SCCP_CONN_ID_MAX); - - llist_for_each_entry(conn, &bsc_gsmnet->subscr_conns, entry) { - if (conn->lcs.lb.state != SUBSCR_SCCP_ST_NONE - && conn->lcs.lb.conn_id == conn_id) - return conn; - } - - return NULL; -} +#include /* Send reset to SMLC */ int bssmap_le_tx_reset(void) @@ -150,6 +135,8 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) { struct osmo_scu_prim *scu_prim = (struct osmo_scu_prim *)oph; struct osmo_sccp_user *scu = _scu; + struct osmo_sccp_instance *sccp = osmo_sccp_get_sccp(scu); + struct bsc_sccp_inst *bsc_sccp = osmo_sccp_get_priv(sccp); struct gsm_subscriber_connection *conn; int rc = 0; @@ -172,7 +159,7 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM): /* Handle inbound confirmation of outbound connection */ DEBUGP(DLCS, "N-CONNECT.cnf(%u)\n", scu_prim->u.connect.conn_id); - conn = get_bsc_conn_by_lb_conn_id(scu_prim->u.connect.conn_id); + conn = bsc_sccp_inst_get_gscon_by_conn_id(bsc_sccp, scu_prim->u.connect.conn_id); if (conn) { conn->lcs.lb.state = SUBSCR_SCCP_ST_CONNECTED; if (msgb_l2len(oph->msg) > 0) { @@ -188,7 +175,7 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) /* Handle incoming connection oriented data */ DEBUGP(DLCS, "N-DATA.ind(%u)\n", scu_prim->u.data.conn_id); - conn = get_bsc_conn_by_lb_conn_id(scu_prim->u.data.conn_id); + conn = bsc_sccp_inst_get_gscon_by_conn_id(bsc_sccp, scu_prim->u.data.conn_id); if (!conn) { LOGP(DLCS, LOGL_ERROR, "N-DATA.ind(%u) for unknown conn_id\n", scu_prim->u.data.conn_id); rc = -EINVAL; @@ -206,7 +193,7 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)), scu_prim->u.disconnect.cause); /* indication of disconnect */ - conn = get_bsc_conn_by_lb_conn_id(scu_prim->u.disconnect.conn_id); + conn = bsc_sccp_inst_get_gscon_by_conn_id(bsc_sccp, scu_prim->u.disconnect.conn_id); if (!conn) { LOGP(DLCS, LOGL_ERROR, "N-DISCONNECT.ind for unknown conn_id %u\n", scu_prim->u.disconnect.conn_id); @@ -234,6 +221,7 @@ static int lb_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg struct osmo_ss7_instance *ss7; uint32_t conn_id; int rc; + struct bsc_sccp_inst *bsc_sccp = osmo_sccp_get_priv(bsc_gsmnet->smlc->sccp); OSMO_ASSERT(conn); OSMO_ASSERT(msg); @@ -244,7 +232,7 @@ static int lb_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg return -EINVAL; } - conn_id = bsc_sccp_inst_next_conn_id(bsc_gsmnet->smlc->sccp); + conn_id = bsc_sccp_inst_next_conn_id(bsc_sccp); if (conn_id == SCCP_CONN_ID_UNSET) { LOGPFSMSL(conn->fi, DLCS, LOGL_ERROR, "Unable to allocate SCCP Connection ID for BSSMAP-LE to SMLC\n"); return -ENOSPC; @@ -421,6 +409,7 @@ static int lb_start(void) enum osmo_ss7_asp_protocol used_proto = OSMO_SS7_ASP_PROT_M3UA; char inst_name[32]; const char *smlc_name = "smlc"; + struct bsc_sccp_inst *bsc_sccp; /* Already set up? */ if (bsc_gsmnet->smlc->sccp_user) @@ -454,6 +443,9 @@ static int lb_start(void) if (!sccp) return -EINVAL; bsc_gsmnet->smlc->sccp = sccp; + bsc_sccp = bsc_sccp_inst_alloc(tall_bsc_ctx); + bsc_sccp->sccp = sccp; + osmo_sccp_set_priv(sccp, bsc_sccp); /* If unset, use default local SCCP address */ if (!bsc_gsmnet->smlc->bsc_addr.presence) diff --git a/src/osmo-bsc/osmo_bsc_sigtran.c b/src/osmo-bsc/osmo_bsc_sigtran.c index 973832b23..830b72e87 100644 --- a/src/osmo-bsc/osmo_bsc_sigtran.c +++ b/src/osmo-bsc/osmo_bsc_sigtran.c @@ -45,26 +45,6 @@ static struct llist_head *msc_list; #define DEFAULT_ASP_LOCAL_IP "localhost" #define DEFAULT_ASP_REMOTE_IP "localhost" -/* Helper function to Check if the given connection id is already assigned */ -static struct gsm_subscriber_connection *get_bsc_conn_by_conn_id(const struct osmo_sccp_user *scu, uint32_t conn_id) -{ - struct gsm_subscriber_connection *conn; - const struct osmo_sccp_instance *sccp = osmo_sccp_get_sccp(scu); - - /* Range (0..SCCP_CONN_ID_MAX) expected, see bsc_sccp_inst_next_conn_id() */ - OSMO_ASSERT(conn_id <= SCCP_CONN_ID_MAX); - - llist_for_each_entry(conn, &bsc_gsmnet->subscr_conns, entry) { - if (conn->sccp.msc && conn->sccp.msc->a.sccp != sccp) - continue; - if (conn->sccp.conn_id != conn_id) - continue; - return conn; - } - - return NULL; -} - struct gsm_subscriber_connection *bsc_conn_by_bsub(const struct bsc_subscr *bsub) { struct gsm_subscriber_connection *conn; @@ -170,10 +150,12 @@ static int handle_unitdata_from_msc(const struct osmo_sccp_addr *msc_addr, struc static int handle_n_connect_from_msc(struct osmo_sccp_user *scu, struct osmo_scu_prim *scu_prim) { struct bsc_msc_data *msc = get_msc_by_addr(&scu_prim->u.connect.calling_addr); + struct osmo_sccp_instance *sccp = osmo_sccp_get_sccp(scu); + struct bsc_sccp_inst *bsc_sccp = osmo_sccp_get_priv(sccp); struct gsm_subscriber_connection *conn; int rc = 0; - conn = get_bsc_conn_by_conn_id(scu, scu_prim->u.connect.conn_id); + conn = bsc_sccp_inst_get_gscon_by_conn_id(bsc_sccp, scu_prim->u.connect.conn_id); if (conn) { LOGP(DMSC, LOGL_NOTICE, "(calling_addr=%s conn_id=%u) N-CONNECT.ind with already used conn_id, ignoring\n", @@ -202,6 +184,13 @@ static int handle_n_connect_from_msc(struct osmo_sccp_user *scu, struct osmo_scu return -ENOMEM; conn->sccp.msc = msc; conn->sccp.conn_id = scu_prim->u.connect.conn_id; + if (bsc_sccp_inst_register_gscon(bsc_sccp, conn) < 0) { + LOGP(DMSC, LOGL_NOTICE, "(calling_addr=%s conn_id=%u) N-CONNECT.ind failed registering conn\n", + osmo_sccp_addr_dump(&scu_prim->u.connect.calling_addr), scu_prim->u.connect.conn_id); + osmo_fsm_inst_term(conn->fi, OSMO_FSM_TERM_REQUEST, NULL); + rc = -ENOENT; + goto refuse; + } /* Take actions asked for by the enclosed PDU */ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CONN_IND, scu_prim); @@ -217,6 +206,8 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) { struct osmo_scu_prim *scu_prim = (struct osmo_scu_prim *)oph; struct osmo_sccp_user *scu = _scu; + struct osmo_sccp_instance *sccp = osmo_sccp_get_sccp(scu); + struct bsc_sccp_inst *bsc_sccp = osmo_sccp_get_priv(sccp); struct gsm_subscriber_connection *conn; int rc = 0; @@ -237,7 +228,7 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) /* Handle outbound connection confirmation */ DEBUGP(DMSC, "N-CONNECT.cnf(%u, %s)\n", scu_prim->u.connect.conn_id, osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); - conn = get_bsc_conn_by_conn_id(scu, scu_prim->u.connect.conn_id); + conn = bsc_sccp_inst_get_gscon_by_conn_id(bsc_sccp, scu_prim->u.connect.conn_id); if (conn) { osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CONN_CFM, scu_prim); conn->sccp.state = SUBSCR_SCCP_ST_CONNECTED; @@ -256,7 +247,7 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); /* Incoming data is a sign of a vital connection */ - conn = get_bsc_conn_by_conn_id(scu, scu_prim->u.data.conn_id); + conn = bsc_sccp_inst_get_gscon_by_conn_id(bsc_sccp, scu_prim->u.data.conn_id); if (conn) { a_reset_conn_success(conn->sccp.msc); handle_data_from_msc(conn, oph->msg); @@ -268,7 +259,7 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)), scu_prim->u.disconnect.cause); /* indication of disconnect */ - conn = get_bsc_conn_by_conn_id(scu, scu_prim->u.disconnect.conn_id); + conn = bsc_sccp_inst_get_gscon_by_conn_id(bsc_sccp, scu_prim->u.disconnect.conn_id); if (conn) { conn->sccp.state = SUBSCR_SCCP_ST_NONE; if (msgb_l2len(oph->msg) > 0) @@ -323,6 +314,7 @@ __attribute__((weak)) int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_conne { struct osmo_ss7_instance *ss7; struct bsc_msc_data *msc; + struct bsc_sccp_inst *bsc_sccp; uint32_t conn_id; int rc; @@ -338,11 +330,16 @@ __attribute__((weak)) int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_conne return -EINVAL; } - conn->sccp.conn_id = conn_id = bsc_sccp_inst_next_conn_id(conn->sccp.msc->a.sccp); + bsc_sccp = osmo_sccp_get_priv(msc->a.sccp); + conn->sccp.conn_id = conn_id = bsc_sccp_inst_next_conn_id(bsc_sccp); if (conn->sccp.conn_id == SCCP_CONN_ID_UNSET) { LOGP(DMSC, LOGL_ERROR, "Unable to allocate SCCP Connection ID\n"); return -1; } + if (bsc_sccp_inst_register_gscon(bsc_sccp, conn) < 0) { + LOGP(DMSC, LOGL_ERROR, "Unable to register SCCP connection (id=%u)\n", conn->sccp.conn_id); + return -1; + } LOGP(DMSC, LOGL_DEBUG, "Allocated new connection id: %u\n", conn->sccp.conn_id); ss7 = osmo_ss7_instance_find(msc->a.cs7_instance); OSMO_ASSERT(ss7); @@ -520,6 +517,7 @@ int osmo_bsc_sigtran_init(struct llist_head *mscs) int prev_msc_nr; struct osmo_sccp_instance *sccp; + struct bsc_sccp_inst *bsc_sccp; llist_for_each_entry(msc, msc_list, entry) { /* An MSC with invalid cs7 instance id defaults to cs7 instance 0 */ @@ -563,6 +561,10 @@ int osmo_bsc_sigtran_init(struct llist_head *mscs) if (!sccp) return -EINVAL; + bsc_sccp = bsc_sccp_inst_alloc(tall_bsc_ctx); + bsc_sccp->sccp = sccp; + osmo_sccp_set_priv(sccp, bsc_sccp); + /* Now that the SCCP client is set up, configure all MSCs on this cs7 instance to use it */ llist_for_each_entry(msc, msc_list, entry) { char msc_name[32];