pespin has submitted this change. ( https://gerrit.osmocom.org/c/osmo-bsc/+/41400?usp=email )
Change subject: sccplite: Handle MGCP/CTRL over SCCPLite multiplex using MTP-TRANSFER.req/ind ......................................................................
sccplite: Handle MGCP/CTRL over SCCPLite multiplex using MTP-TRANSFER.req/ind
This is needed ie. in SCCPLite, where MGCP and CTRL messages need to be sent/received over the same IPA multiplex TCP conn as the SCCPLite (SCCP/IPA) one towards a given MSC.
Prior to this, osmo-bsc-sccplite was doing lots of manual libosmo-sigtran APIs to obtain a route, an AS, and ASP, sending over an ASP, etc. Similary, the rx_unknown callback was added to libosmo-sigtran to hook into the ASP level. Tons of stuff which should really be done properly inside libosmo-sigtran and provided over the existing public SAPs, as this patch proposes.
With the proposed approach, an SCCPLite client (eg. osmo-bsc), besides setting up an sccp_user to handle SCCP, also sets an ss7_user (MTP SAP) with the newly introduced new Service Indicator (SI) MTP_SI_NI11_OSMO_IPA to be able to manage the IPA[MGCP/CTRL] traffic towards a given MTP peer (SCCPLite client, ie. MSC).
Related: SYS#6880 Depends: libosmo-sigtran.git Change-Id I9fedb26ccd3434fc7f272feb3c45cf4bdb80c7ae Change-Id: I567ed206eab1add21836bfd937f3790d3d7a00d7 --- M TODO-RELEASE M include/osmocom/bsc/bsc_msc_data.h M include/osmocom/bsc/osmo_bsc_sigtran.h M src/osmo-bsc/bsc_ctrl.c M src/osmo-bsc/osmo_bsc_mgcp.c M src/osmo-bsc/osmo_bsc_sigtran.c 6 files changed, 158 insertions(+), 116 deletions(-)
Approvals: fixeria: Looks good to me, approved Jenkins Builder: Verified
diff --git a/TODO-RELEASE b/TODO-RELEASE index 400e0e0..a7b28e3 100644 --- a/TODO-RELEASE +++ b/TODO-RELEASE @@ -10,3 +10,6 @@ libosmo-mgcp-client bump_dep Depend on I0d58e6d84418f50670c8ab7cf8490af3bc2f5c26 libosmo-sigtran >2.1.0 osmo_sccp_addr_{create,update}() libosmo-sigtran minor_release>2.1.0 Avoid leaking msgb during asp_rx_unknown() +libosmo-sigtran minor_release>2.1.0 osmo_ss7_instance_get_network_indicator(), + osmo_mtp_prim_xfer_req_prepend(), + MTP_SI_NI11_OSMO_IPA diff --git a/include/osmocom/bsc/bsc_msc_data.h b/include/osmocom/bsc/bsc_msc_data.h index 1968aab..f3cf28a 100644 --- a/include/osmocom/bsc/bsc_msc_data.h +++ b/include/osmocom/bsc/bsc_msc_data.h @@ -175,6 +175,7 @@ bool cs7_instance_valid; struct osmo_sccp_instance *sccp; struct osmo_sccp_user *sccp_user; + struct osmo_ss7_user *mtp_user;
/* IPA or M3UA or SUA? */ enum osmo_ss7_asp_protocol asp_proto; diff --git a/include/osmocom/bsc/osmo_bsc_sigtran.h b/include/osmocom/bsc/osmo_bsc_sigtran.h index 2ee35e5..34c6e4d 100644 --- a/include/osmocom/bsc/osmo_bsc_sigtran.h +++ b/include/osmocom/bsc/osmo_bsc_sigtran.h @@ -45,10 +45,10 @@ void osmo_bsc_sigtran_tx_reset_ack(const struct bsc_msc_data *msc);
/* receive + process a CTRL command from the piggy-back on the IPA/SCCPlite link */ -int bsc_sccplite_rx_ctrl(struct osmo_ss7_asp *asp, struct msgb *msg); +int bsc_sccplite_rx_ctrl(struct bsc_msc_data *msc, struct msgb *msg);
/* receive + process a MGCP message from the piggy-back on the IPA/SCCPlite link */ -int bsc_sccplite_rx_mgcp(struct osmo_ss7_asp *asp, struct msgb *msg); +int bsc_sccplite_rx_mgcp(struct bsc_msc_data *msc, struct msgb *msg);
/* send a message via SCCPLite to given MSC */ int bsc_sccplite_msc_send(struct bsc_msc_data *msc, struct msgb *msg); diff --git a/src/osmo-bsc/bsc_ctrl.c b/src/osmo-bsc/bsc_ctrl.c index 6c0150b..a075619 100644 --- a/src/osmo-bsc/bsc_ctrl.c +++ b/src/osmo-bsc/bsc_ctrl.c @@ -32,6 +32,10 @@ #include <osmocom/vty/misc.h>
#include <osmocom/gsm/gsm48.h> + +#include <osmocom/sigtran/protocol/mtp.h> +#include <osmocom/sigtran/mtp_sap.h> + #include <osmocom/bsc/ipaccess.h> #include <osmocom/bsc/gsm_data.h> #include <osmocom/bsc/abis_nm.h> @@ -628,69 +632,40 @@ return 0; }
-/* Obtain SS7 application server currently handling given MSC (DPC) */ -static struct osmo_ss7_as *msc_get_ss7_as(struct bsc_msc_data *msc) -{ - struct osmo_ss7_route *rt; - struct osmo_ss7_as *as; - struct osmo_ss7_instance *ss7 = osmo_sccp_get_ss7(msc->a.sccp); - rt = osmo_ss7_route_lookup(ss7, msc->a.msc_addr.pc); - if (!rt) - return NULL; - as = osmo_ss7_route_get_dest_as(rt); - if (!as) - return NULL; - return as; -} - int bsc_sccplite_msc_send(struct bsc_msc_data *msc, struct msgb *msg) { - struct osmo_ss7_as *as; - struct osmo_ss7_asp *asp; + struct osmo_ss7_instance *ss7 = osmo_sccp_get_ss7(msc->a.sccp); + struct osmo_mtp_prim *omp; + struct osmo_mtp_transfer_param *param;
- as = msc_get_ss7_as(msc); - if (!as) { - msgb_free(msg); - return -1; - } + /* Wrap into MTP-TRANSFER.req primitive */ + omp = osmo_mtp_prim_xfer_req_prepend(NULL, msg); + OSMO_ASSERT(omp); + param = &omp->u.transfer; + param->opc = osmo_ss7_instance_get_primary_pc(ss7); + param->dpc = msc->a.msc_addr.pc; + param->sls = 0; + param->sio = MTP_SIO(MTP_SI_NI11_OSMO_IPA, osmo_ss7_instance_get_network_indicator(ss7));
- /* don't attempt to send CTRL on a non-SCCPlite AS */ - if (osmo_ss7_as_get_asp_protocol(as) != OSMO_SS7_ASP_PROT_IPA) { - msgb_free(msg); - return 0; - } - - asp = osmo_ss7_as_select_asp(as); - if (!asp) { - LOGP(DCTRL, LOGL_NOTICE, "No ASP found for AS, dropping message\n"); - msgb_free(msg); - return -1; - } - return osmo_ss7_asp_send(asp, msg); + /* 3) send via MTP Transfer SAP (osmo_ss7_instance) */ + return osmo_ss7_user_mtp_sap_prim_down(msc->a.mtp_user, omp); }
/* Encode a CTRL command and send it to the given ASP - * \param[in] asp ASP through which we shall send the encoded message + * \param[in] msc MSC through which we shall send the encoded message * \param[in] cmd decoded CTRL command to be encoded and sent. Ownership is *NOT* * transferred, to permit caller to send the same CMD to several ASPs. * Caller must hence free 'cmd' itself. * \returns 0 on success; negative on error */ -static int sccplite_asp_ctrl_cmd_send(struct osmo_ss7_asp *asp, struct ctrl_cmd *cmd) +static int bsc_sccplite_msc_ctrl_cmd_send(struct bsc_msc_data *msc, struct ctrl_cmd *cmd) { /* this is basically like libosmoctrl:ctrl_cmd_send(), not for a dedicated * CTRL connection but for the CTRL piggy-back on the IPA/SCCPlite link */ - struct msgb *msg; - - /* don't attempt to send CTRL on a non-SCCPlite ASP */ - if (osmo_ss7_asp_get_proto(asp) != OSMO_SS7_ASP_PROT_IPA) - return 0; - - msg = ctrl_cmd_make(cmd); + struct msgb *msg = ctrl_cmd_make(cmd); if (!msg) return -1; - osmo_ipa_msg_push_headers(msg, IPAC_PROTO_OSMO, IPAC_PROTO_EXT_CTRL); - return osmo_ss7_asp_send(asp, msg); + return bsc_sccplite_msc_send(msc, msg); }
/* Ownership of 'cmd' is *NOT* transferred, to permit caller to send the same CMD to several ASPs. @@ -709,7 +684,7 @@
/* receive + process a CTRL command from the piggy-back on the IPA/SCCPlite link. * msg is owned by the caller. */ -int bsc_sccplite_rx_ctrl(struct osmo_ss7_asp *asp, struct msgb *msg) +int bsc_sccplite_rx_ctrl(struct bsc_msc_data *msc, struct msgb *msg) { struct ctrl_cmd *cmd; bool parse_failed; @@ -719,7 +694,7 @@ OSMO_ASSERT(msg->l2h);
/* prase raw (ASCII) CTRL command into ctrl_cmd */ - cmd = ctrl_cmd_parse3(asp, msg, &parse_failed); + cmd = ctrl_cmd_parse3(msc, msg, &parse_failed); OSMO_ASSERT(cmd); if (cmd->type == CTRL_TYPE_ERROR && parse_failed) goto send_reply; @@ -728,7 +703,7 @@ ctrl_cmd_handle(bsc_gsmnet->ctrl, cmd, bsc_gsmnet);
send_reply: - rc = sccplite_asp_ctrl_cmd_send(asp, cmd); + rc = bsc_sccplite_msc_ctrl_cmd_send(msc, cmd); talloc_free(cmd); return rc; } diff --git a/src/osmo-bsc/osmo_bsc_mgcp.c b/src/osmo-bsc/osmo_bsc_mgcp.c index 978ff8c..8eee71f 100644 --- a/src/osmo-bsc/osmo_bsc_mgcp.c +++ b/src/osmo-bsc/osmo_bsc_mgcp.c @@ -39,23 +39,6 @@
#include <arpa/inet.h>
-/* Determine MSC based on the ASP over which the message was received */ -static struct bsc_msc_data *msc_from_asp(struct osmo_ss7_asp *asp) -{ - int msc_nr; - const char *asp_name = osmo_ss7_asp_get_name(asp); - /* this is rather ugly, as we of course have MTP-level routing between - * the local SCCP user (BSC) and the AS/ASPs. However, for the most simple - * SCCPlite case, there is a 1:1 mapping between ASP and AS, and using - * the libosmo-sigtran "simple client", the names are "as[p]-clnt-msc-%u", - * as set in osmo_bsc_sigtran_init() */ - if (!asp_name || sscanf(asp_name, "asp-clnt-msc-%u", &msc_nr) != 1) { - LOGP(DMSC, LOGL_ERROR, "Cannot find to which MSC the ASP '%s' belongs\n", asp_name); - return NULL; - } - return osmo_msc_data_find(bsc_gsmnet, msc_nr); -} - /* negative on error, zero upon success */ static int parse_local_endpoint_name(char *buf, size_t buf_len, const char *data) { @@ -92,9 +75,8 @@ }
/* We received an IPA-encapsulated MGCP message from a MSC. msg owned by caller. */ -int bsc_sccplite_rx_mgcp(struct osmo_ss7_asp *asp, struct msgb *msg) +int bsc_sccplite_rx_mgcp(struct bsc_msc_data *msc, struct msgb *msg) { - struct bsc_msc_data *msc; struct gsm_subscriber_connection *conn; char rcv_ep_local_name[1024]; struct osmo_sockaddr_str osa_str = {}; @@ -103,17 +85,11 @@ struct mgcp_client *mgcp_cli = NULL; int rc;
- LOGP(DMSC, LOGL_INFO, "%s: Received IPA-encapsulated MGCP: %s\n", - osmo_ss7_asp_get_name(asp), msg->l2h); - - msc = msc_from_asp(asp); - if (!msc) - return 0; + LOG_MSC(msc, LOGL_INFO, "Received IPA-encapsulated MGCP: %s\n", msg->l2h);
rc = parse_local_endpoint_name(rcv_ep_local_name, sizeof(rcv_ep_local_name), (const char *)msg->l2h); if (rc < 0) { - LOGP(DMSC, LOGL_ERROR, "(%s:) Received IPA-encapsulated MGCP: Failed to parse CIC\n", - osmo_ss7_asp_get_name(asp)); + LOG_MSC(msc, LOGL_ERROR, "Received IPA-encapsulated MGCP: Failed to parse CIC\n"); return rc; }
@@ -141,26 +117,25 @@ }
if (!mgcp_cli) { - LOGP(DMSC, LOGL_ERROR, "(%s:) Received IPA-encapsulated MGCP: Failed to find associated MGW\n", - osmo_ss7_asp_get_name(asp)); + LOG_MSC(msc, LOGL_ERROR, "Received IPA-encapsulated MGCP: Failed to find associated MGW\n"); return 0; }
rc = osmo_sockaddr_str_from_str(&osa_str, mgcp_client_remote_addr_str(mgcp_cli), mgcp_client_remote_port(mgcp_cli)); if (rc < 0) { - LOGP(DMSC, LOGL_ERROR, "(%s:) Received IPA-encapsulated MGCP: Failed to parse MGCP address %s:%u\n", - osmo_ss7_asp_get_name(asp), mgcp_client_remote_addr_str(mgcp_cli), mgcp_client_remote_port(mgcp_cli)); + LOG_MSC(msc, LOGL_ERROR, "Received IPA-encapsulated MGCP: Failed to parse MGCP address %s:%u\n", + mgcp_client_remote_addr_str(mgcp_cli), mgcp_client_remote_port(mgcp_cli)); return rc; }
- LOGP(DMSC, LOGL_NOTICE, "%s: Forwarding IPA-encapsulated MGCP to MGW at " OSMO_SOCKADDR_STR_FMT "\n", - osmo_ss7_asp_get_name(asp), OSMO_SOCKADDR_STR_FMT_ARGS_NOT_NULL(&osa_str)); + LOG_MSC(msc, LOGL_NOTICE, "Forwarding IPA-encapsulated MGCP to MGW at " OSMO_SOCKADDR_STR_FMT "\n", + OSMO_SOCKADDR_STR_FMT_ARGS_NOT_NULL(&osa_str));
rc = osmo_sockaddr_str_to_sockaddr(&osa_str, &osa.u.sas); if (rc < 0) { - LOGP(DMSC, LOGL_ERROR, "(%s:) Received IPA-encapsulated MGCP: Failed to parse MGCP address " OSMO_SOCKADDR_STR_FMT "\n", - osmo_ss7_asp_get_name(asp), OSMO_SOCKADDR_STR_FMT_ARGS_NOT_NULL(&osa_str)); + LOG_MSC(msc, LOGL_ERROR, "Received IPA-encapsulated MGCP: Failed to parse MGCP address " OSMO_SOCKADDR_STR_FMT "\n", + OSMO_SOCKADDR_STR_FMT_ARGS_NOT_NULL(&osa_str)); return rc; } dest_len = osmo_sockaddr_size(&osa); @@ -182,7 +157,7 @@ if (!(what & OSMO_FD_READ)) return 0;
- msg = msgb_alloc_headroom(1024, 16, "MGCP->IPA"); + msg = msgb_alloc_headroom(1400, 64, "MGCP->IPA"); OSMO_ASSERT(msg); rc = recv(ofd->fd, msg->data, msgb_tailroom(msg), 0); if (rc <= 0) { diff --git a/src/osmo-bsc/osmo_bsc_sigtran.c b/src/osmo-bsc/osmo_bsc_sigtran.c index cc6ac5e..db8b6f3 100644 --- a/src/osmo-bsc/osmo_bsc_sigtran.c +++ b/src/osmo-bsc/osmo_bsc_sigtran.c @@ -22,6 +22,8 @@ #include <osmocom/core/fsm.h> #include <osmocom/sigtran/osmo_ss7.h> #include <osmocom/sigtran/sccp_sap.h> +#include <osmocom/sigtran/protocol/mtp.h> +#include <osmocom/sigtran/mtp_sap.h> #include <osmocom/sccp/sccp_types.h> #include <osmocom/core/linuxlist.h> #include <osmocom/gsm/gsm0808.h> @@ -418,6 +420,106 @@ return rc; }
+/* Determine MSC based on OPC/DPC of received message. */ +static struct bsc_msc_data *msc_from_mtp_label(const struct osmo_mtp_transfer_param *param) +{ + struct bsc_msc_data *msc; + llist_for_each_entry(msc, &bsc_gsmnet->mscs, entry) { + if (msc->a.bsc_addr.pc != param->dpc) + continue; + if (msc->a.msc_addr.pc != param->opc) + continue; + return msc; + } + + return NULL; +} + +/* Callback function, called by the MTP stack for MGCP/CTRL over IPA from MSC (SCCPlite). + * msgb ownership is transferred to this callback and hence must be freed. */ +static int mtp_sap_up(struct osmo_prim_hdr *oph, void *data) +{ + struct osmo_mtp_prim *mtp_prim = (struct osmo_mtp_prim *)oph; + struct osmo_ss7_instance *s7i = data; + struct msgb *msg = oph->msg; + struct bsc_msc_data *msc; + struct ipa_head *ih; + struct ipa_head_ext *ihext; + int rc = 0; + + switch (OSMO_PRIM_HDR(&mtp_prim->oph)) { + case OSMO_PRIM(OSMO_MTP_PRIM_TRANSFER, PRIM_OP_INDICATION): + LOGP(DMSC, LOGL_DEBUG, "MTP-User-SAP: %s: %s\n", + osmo_mtp_prim_name(oph), msgb_hexdump(msg)); + + OSMO_ASSERT((mtp_prim->u.transfer.sio & 0xF) == MTP_SI_NI11_OSMO_IPA); + + msc = msc_from_mtp_label(&mtp_prim->u.transfer); + if (!msc) { + char str_opc[32]; + char str_dpc[32]; + LOGP(DMSC, LOGL_NOTICE, "MTP-User-SAP: %s: Unknown MSC OPC=%u=%s DPC=%u=%s\n", + osmo_mtp_prim_name(oph), + mtp_prim->u.transfer.opc, + osmo_ss7_pointcode_print_buf(str_opc, sizeof(str_opc), s7i, mtp_prim->u.transfer.opc), + mtp_prim->u.transfer.dpc, + osmo_ss7_pointcode_print_buf(str_dpc, sizeof(str_dpc), s7i, mtp_prim->u.transfer.dpc)); + rc = -ENOENT; + goto ret_free; + } + msgb_pull_to_l2(msg); + + ih = (struct ipa_head *)msgb_data(msg); + osmo_ipa_msgb_cb_proto(msg) = ih->proto; + msg->l2h = msgb_pull(msg, sizeof(struct ipa_head)); + + switch (osmo_ipa_msgb_cb_proto(msg)) { + case IPAC_PROTO_OSMO: + ihext = (struct ipa_head_ext *)msgb_data(msg); + osmo_ipa_msgb_cb_proto_ext(msg) = ihext->proto; + msg->l2h = msgb_pull(msg, sizeof(struct ipa_head_ext)); + + switch (osmo_ipa_msgb_cb_proto_ext(msg)) { + case IPAC_PROTO_EXT_CTRL: + rc = bsc_sccplite_rx_ctrl(msc, msg); + break; + case IPAC_PROTO_EXT_MGCP: + rc = bsc_sccplite_rx_mgcp(msc, msg); + break; + } + break; + case IPAC_PROTO_MGCP_OLD: + rc = bsc_sccplite_rx_mgcp(msc, msg); + break; + default: + LOG_MSC(msc, LOGL_NOTICE, "MTP-User-SAP: %s: Unexpected IPA proto %u\n", + osmo_mtp_prim_name(oph), ih->proto); + break; + } + break; + case OSMO_PRIM(OSMO_MTP_PRIM_PAUSE, PRIM_OP_INDICATION): + LOGP(DMSC, LOGL_NOTICE, "MTP-User-SAP: Unhandled %s\n", + osmo_mtp_prim_name(oph)); + break; + case OSMO_PRIM(OSMO_MTP_PRIM_RESUME, PRIM_OP_INDICATION): + LOGP(DMSC, LOGL_NOTICE, "MTP-User-SAP: Unhandled %s\n", + osmo_mtp_prim_name(oph)); + break; + case OSMO_PRIM(OSMO_MTP_PRIM_STATUS, PRIM_OP_INDICATION): + LOGP(DMSC, LOGL_NOTICE, "MTP-User-SAP: Unhandled %s\n", + osmo_mtp_prim_name(oph)); + break; + default: + LOGP(DMSC, LOGL_ERROR, "MTP-User-SAP: Unhandled %s\n", + osmo_mtp_prim_name(oph)); + break; + } + +ret_free: + msgb_free(oph->msg); + return rc; +} + /* Allocate resources to make a new connection oriented sigtran connection * (not the connection ittself!) */ enum bsc_con osmo_bsc_sigtran_new_conn(struct gsm_subscriber_connection *conn, struct bsc_msc_data *msc) @@ -591,8 +693,6 @@ /* Default point-code to be used as remote address (MSC) */ #define MSC_DEFAULT_ADDR_NAME "addr-dyn-msc-default"
-static int asp_rx_unknown(struct osmo_ss7_asp *asp, int ppid_mux, struct msgb *msg); - /* Initialize osmo sigtran backhaul */ int osmo_bsc_sigtran_init(struct llist_head *mscs) { @@ -601,8 +701,6 @@ struct llist_head *ll_it; int rc;
- osmo_ss7_register_rx_unknown_cb(&asp_rx_unknown); - OSMO_ASSERT(mscs); msc_list = mscs;
@@ -783,6 +881,21 @@ if (!msc->a.sccp_user) return -EINVAL;
+ if (msc_is_sccplite(msc)) { + /* Bind MTP user to Tx/RxMGCP/CTRL over the SCCPlite IPA multiplex. + * Bind only one user per osmo_ss7_instance. */ + struct osmo_ss7_instance *s7i = osmo_sccp_get_ss7(msc->a.sccp); + msc->a.mtp_user = osmo_ss7_user_find_by_si(s7i, MTP_SI_NI11_OSMO_IPA); + if (!msc->a.mtp_user) { + msc->a.mtp_user = osmo_ss7_user_create(s7i, "MGCP/CTRL-IPA"); + osmo_ss7_user_set_prim_cb(msc->a.mtp_user, mtp_sap_up); + osmo_ss7_user_set_priv(msc->a.mtp_user, s7i); + osmo_ss7_user_register(msc->a.mtp_user, MTP_SI_NI11_OSMO_IPA); + } + if (!msc->a.mtp_user) + return -EINVAL; + } + /* Start MSC-Reset procedure */ a_reset_alloc(msc, msc_name); } @@ -790,28 +903,3 @@
return 0; } - -/* this function receives all messages received on an ASP for a PPID / StreamID that - * libosmo-sigtran doesn't know about, such as piggy-backed CTRL and/or MGCP. - * msg is owned by the caller, ie. ownership is not transferred to this callback. */ -static int asp_rx_unknown(struct osmo_ss7_asp *asp, int ppid_mux, struct msgb *msg) -{ - if (osmo_ss7_asp_get_proto(asp) != OSMO_SS7_ASP_PROT_IPA) - return 0; - - switch (ppid_mux) { - case IPAC_PROTO_OSMO: - switch (osmo_ipa_msgb_cb_proto_ext(msg)) { - case IPAC_PROTO_EXT_CTRL: - return bsc_sccplite_rx_ctrl(asp, msg); - case IPAC_PROTO_EXT_MGCP: - return bsc_sccplite_rx_mgcp(asp, msg); - } - break; - case IPAC_PROTO_MGCP_OLD: - return bsc_sccplite_rx_mgcp(asp, msg); - default: - break; - } - return 0; /* OSMO_SS7_UNKNOWN? */ -}