aboutsummaryrefslogtreecommitdiffstats
path: root/src/libmsc/sccp_ran.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libmsc/sccp_ran.c')
-rw-r--r--src/libmsc/sccp_ran.c216
1 files changed, 216 insertions, 0 deletions
diff --git a/src/libmsc/sccp_ran.c b/src/libmsc/sccp_ran.c
new file mode 100644
index 000000000..38ece550e
--- /dev/null
+++ b/src/libmsc/sccp_ran.c
@@ -0,0 +1,216 @@
+/*
+ * (C) 2019 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <osmocom/core/logging.h>
+
+#include <osmocom/sccp/sccp_types.h>
+#include <osmocom/sigtran/sccp_sap.h>
+#include <osmocom/sigtran/sccp_helpers.h>
+
+#include <osmocom/msc/debug.h>
+#include <osmocom/msc/sccp_ran.h>
+#include <osmocom/msc/ran_infra.h>
+
+struct osmo_tdef g_sccp_tdefs[] = {
+ {}
+};
+
+static int sccp_ran_sap_up(struct osmo_prim_hdr *oph, void *_scu);
+
+struct sccp_ran_inst *sccp_ran_init(void *talloc_ctx, struct osmo_sccp_instance *sccp, enum osmo_sccp_ssn ssn,
+ const char *sccp_user_name, struct ran_infra *ran, void *user_data)
+{
+ struct sccp_ran_inst *sri = talloc_zero(talloc_ctx, struct sccp_ran_inst);
+ *sri = (struct sccp_ran_inst){
+ .ran = ran,
+ .sccp = sccp,
+ .user_data = user_data,
+ };
+
+ INIT_LLIST_HEAD(&sri->ran_peers);
+ INIT_LLIST_HEAD(&sri->ran_conns);
+
+ osmo_sccp_local_addr_by_instance(&sri->local_sccp_addr, sccp, ssn);
+ sri->scu = osmo_sccp_user_bind(sccp, sccp_user_name, sccp_ran_sap_up, ssn);
+ osmo_sccp_user_set_priv(sri->scu, sri);
+
+ OSMO_ASSERT(!ran->sri);
+ ran->sri = sri;
+
+ return sri;
+}
+
+static int sccp_ran_sap_up(struct osmo_prim_hdr *oph, void *_scu)
+{
+ struct osmo_sccp_user *scu = _scu;
+ struct sccp_ran_inst *sri = osmo_sccp_user_get_priv(scu);
+ struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
+ struct osmo_sccp_addr *my_addr;
+ struct osmo_sccp_addr *peer_addr;
+ uint32_t conn_id;
+ int rc;
+
+ if (!sri->ran || !sri->ran->sccp_ran_ops.up_l2) {
+ LOG_SCCP_RAN_CL(sri, NULL, LOGL_ERROR, "This RAN type is not set up\n");
+ msgb_free(oph->msg);
+ return -1;
+ }
+
+ switch (OSMO_PRIM_HDR(oph)) {
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION):
+ /* indication of new inbound connection request */
+ conn_id = prim->u.connect.conn_id;
+ my_addr = &prim->u.connect.called_addr;
+ peer_addr = &prim->u.connect.calling_addr;
+ LOG_SCCP_RAN_CO(sri, peer_addr, conn_id, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_scu_prim_name(oph));
+
+ if (!msgb_l2(oph->msg) || msgb_l2len(oph->msg) == 0) {
+ LOG_SCCP_RAN_CO(sri, peer_addr, conn_id, LOGL_NOTICE, "Received invalid N-CONNECT.ind\n");
+ rc = -1;
+ break;
+ }
+
+ if (osmo_sccp_addr_ri_cmp(&sri->local_sccp_addr, my_addr))
+ LOG_SCCP_RAN_CO(sri, NULL, conn_id, LOGL_INFO,
+ "Called address is %s which is not the locally configured address\n",
+ osmo_sccp_inst_addr_name(sri->sccp, my_addr));
+
+ /* ensure the local SCCP socket is ACTIVE */
+ osmo_sccp_tx_conn_resp(scu, conn_id, my_addr, NULL, 0);
+
+ rc = sri->ran->sccp_ran_ops.up_l2(sri, peer_addr, true, conn_id, oph->msg);
+ if (rc)
+ osmo_sccp_tx_disconn(scu, conn_id, my_addr, SCCP_RETURN_CAUSE_UNQUALIFIED);
+ break;
+
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):
+ /* connection-oriented data received */
+ conn_id = prim->u.data.conn_id;
+ LOG_SCCP_RAN_CO(sri, NULL, conn_id, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_scu_prim_name(oph));
+
+ rc = sri->ran->sccp_ran_ops.up_l2(sri, NULL, true, conn_id, oph->msg);
+ break;
+
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION):
+ /* indication of disconnect */
+ conn_id = prim->u.disconnect.conn_id;
+ LOG_SCCP_RAN_CO(sri, NULL, conn_id, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_scu_prim_name(oph));
+
+ /* If there is no L2 payload in the N-DISCONNECT, no need to dispatch up_l2(). */
+ if (msgb_l2len(oph->msg))
+ rc = sri->ran->sccp_ran_ops.up_l2(sri, NULL, true, conn_id, oph->msg);
+ else
+ rc = 0;
+
+ /* Make sure the ran_conn is dropped. It might seem more optimal to combine the disconnect() into
+ * up_l2(), but since an up_l2() dispatch might already cause the ran_conn to be discarded for other
+ * reasons, a separate disconnect() with a separate conn_id lookup is actually necessary. */
+ sri->ran->sccp_ran_ops.disconnect(sri, conn_id);
+ break;
+
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
+ /* connection-less data received */
+ my_addr = &prim->u.unitdata.called_addr;
+ peer_addr = &prim->u.unitdata.calling_addr;
+ LOG_SCCP_RAN_CL(sri, peer_addr, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_scu_prim_name(oph));
+
+ if (osmo_sccp_addr_ri_cmp(&sri->local_sccp_addr, my_addr))
+ LOG_SCCP_RAN_CL(sri, NULL, LOGL_INFO,
+ "Called address is %s which is not the locally configured address\n",
+ osmo_sccp_inst_addr_name(sri->sccp, my_addr));
+
+ rc = sri->ran->sccp_ran_ops.up_l2(sri, peer_addr, false, 0, oph->msg);
+ break;
+
+ default:
+ LOG_SCCP_RAN_CL(sri, NULL, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_scu_prim_name(oph));
+ rc = -1;
+ break;
+ }
+
+ msgb_free(oph->msg);
+ return rc;
+}
+
+/* Push some padding if necessary to reach a multiple-of-eight offset to be msgb_push() an osmo_scu_prim that will then
+ * be 8-byte aligned. */
+static void msgb_pad_mod8(struct msgb *msg)
+{
+ uint8_t mod8 = (intptr_t)(msg->data) % 8;
+ if (mod8)
+ msgb_push(msg, mod8);
+}
+
+int sccp_ran_down_l2_co_initial(struct sccp_ran_inst *sri,
+ const struct osmo_sccp_addr *called_addr,
+ uint32_t conn_id, struct msgb *l2)
+{
+ struct osmo_scu_prim *prim;
+
+ l2->l2h = l2->data;
+
+ msgb_pad_mod8(l2);
+ prim = (struct osmo_scu_prim *) msgb_push(l2, sizeof(*prim));
+ prim->u.connect = (struct osmo_scu_connect_param){
+ .called_addr = *called_addr,
+ .calling_addr = sri->local_sccp_addr,
+ .sccp_class = 2,
+ //.importance = ?,
+ .conn_id = conn_id,
+ };
+ osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_REQUEST, l2);
+ return osmo_sccp_user_sap_down_nofree(sri->scu, &prim->oph);
+}
+
+int sccp_ran_down_l2_co(struct sccp_ran_inst *sri, uint32_t conn_id, struct msgb *l2)
+{
+ struct osmo_scu_prim *prim;
+
+ l2->l2h = l2->data;
+
+ msgb_pad_mod8(l2);
+ prim = (struct osmo_scu_prim *) msgb_push(l2, sizeof(*prim));
+ prim->u.data.conn_id = conn_id;
+ osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, l2);
+ return osmo_sccp_user_sap_down_nofree(sri->scu, &prim->oph);
+}
+
+int sccp_ran_down_l2_cl(struct sccp_ran_inst *sri, const struct osmo_sccp_addr *called_addr, struct msgb *l2)
+{
+ struct osmo_scu_prim *prim;
+
+ l2->l2h = l2->data;
+
+ msgb_pad_mod8(l2);
+ prim = (struct osmo_scu_prim *) msgb_push(l2, sizeof(*prim));
+ prim->u.unitdata = (struct osmo_scu_unitdata_param){
+ .called_addr = *called_addr,
+ .calling_addr = sri->local_sccp_addr,
+ };
+ osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_REQUEST, l2);
+ return osmo_sccp_user_sap_down_nofree(sri->scu, &prim->oph);
+}
+
+int sccp_ran_disconnect(struct sccp_ran_inst *sri, uint32_t conn_id, uint32_t cause)
+{
+ return osmo_sccp_tx_disconn(sri->scu, conn_id, NULL, cause);
+}