diff mncc/intswitch.c @ 2:053f04687106

mncc: initial import from old ThemWi
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 08 Jun 2024 23:12:12 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mncc/intswitch.c	Sat Jun 08 23:12:12 2024 +0000
@@ -0,0 +1,233 @@
+/*
+ * In this module we implement internally switched calls,
+ * going from one GSM subscriber to another.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <syslog.h>
+#include "../include/mncc.h"
+#include "../include/gsm48_const.h"
+#include "struct.h"
+#include "gsm_call.h"
+
+extern char *mncc_msg_name();
+
+void
+internal_switch_mo_setup(call, msg)
+	struct gsm_call *call;
+	struct gsm_mncc *msg;
+{
+	struct gsm_call *mt;
+	struct gsm_mncc callproc;
+
+	if (!(msg->fields & MNCC_F_BEARER_CAP)) {
+		syslog(LOG_ERR, "rejecting intsw call 0x%x: no bearer cap",
+			msg->callref);
+		reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
+				GSM48_CC_CAUSE_INVAL_MAND_INF);
+		call->gc_flag = 1;
+		return;
+	}
+	/* same speech-only restriction as in OsmoMSC's mncc_builtin */
+	if (msg->bearer_cap.transfer != GSM48_BCAP_ITCAP_SPEECH ||
+	    msg->bearer_cap.mode != GSM48_BCAP_TMOD_CIRCUIT) {
+		syslog(LOG_ERR, "rejecting intsw call 0x%x: bad bearer cap",
+			msg->callref);
+		reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
+				GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
+		call->gc_flag = 1;
+		return;
+	}
+	mt = create_new_mt_call();
+	if (!mt) {
+		syslog(LOG_ERR,
+			"rejecting intsw call 0x%x: no memory for MT call",
+			msg->callref);
+		reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU,
+				GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
+		call->gc_flag = 1;
+		return;
+	}
+	syslog(LOG_DEBUG, "intsw: MO=0x%x MT=0x%x", call->callref, mt->callref);
+	call->other_leg = mt;
+	mt->other_leg = call;
+	/* send call proceeding */
+	bzero(&callproc, sizeof(struct gsm_mncc));
+	callproc.msg_type = MNCC_CALL_PROC_REQ;
+	callproc.callref = call->callref;
+	send_mncc_to_gsm(&callproc, sizeof(struct gsm_mncc));
+	/* turn MNCC_SETUP_IND into MNCC_SETUP_REQ for MT */
+	msg->msg_type = MNCC_SETUP_REQ;
+	msg->callref = mt->callref;
+	msg->imsi[0] = '\0';
+	send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
+}
+
+static void
+handle_setup_cnf(call, msg)
+	struct gsm_call *call;
+	struct gsm_mncc *msg;
+{
+	struct gsm_mncc ack;
+	struct gsm_mncc_bridge bridge;
+
+	/* acknowledge connect */
+	bzero(&ack, sizeof(struct gsm_mncc));
+	ack.msg_type = MNCC_SETUP_COMPL_REQ;
+	ack.callref = call->callref;
+	send_mncc_to_gsm(&ack, sizeof(struct gsm_mncc));
+	/* do we have the far end? */
+	if (!call->other_leg) {
+		syslog(LOG_ERR, "intsw: missing other leg for MNCC_SETUP_CNF");
+		return;
+	}
+	syslog(LOG_DEBUG, "MNCC_SETUP_CNF from 0x%x to 0x%x", call->callref,
+		call->other_leg->callref);
+	msg->msg_type = MNCC_SETUP_RSP;
+	msg->callref = call->other_leg->callref;
+	send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
+	/* bridge TCH */
+	bzero(&bridge, sizeof(struct gsm_mncc_bridge));
+	bridge.msg_type = MNCC_BRIDGE;
+	bridge.callref[0] = call->callref;
+	bridge.callref[1] = call->other_leg->callref;
+	send_mncc_to_gsm(&bridge, sizeof(struct gsm_mncc_bridge));
+}
+
+static void
+forward_to_remote(call, msg, new_msg_type)
+	struct gsm_call *call;
+	struct gsm_mncc *msg;
+	uint32_t new_msg_type;
+{
+	if (!call->other_leg) {
+		syslog(LOG_ERR, "intsw: missing other leg for msg forwarding");
+		/* drop it like OsmoMSC's mncc_builtin does */
+		return;
+	}
+	msg->msg_type = new_msg_type;
+	msg->callref = call->other_leg->callref;
+	send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
+}
+
+static void
+handle_disconnect(call, msg)
+	struct gsm_call *call;
+	struct gsm_mncc *msg;
+{
+	struct gsm_call *remote;
+
+	/* release on near end */
+	msg->msg_type = MNCC_REL_REQ;
+	send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
+	/* disconnect on far end */
+	remote = call->other_leg;
+	if (!remote) {
+		syslog(LOG_ERR, "intsw: missing other leg for MNCC_DISC_IND");
+		return;
+	}
+	syslog(LOG_DEBUG, "MNCC_DISC_IND from 0x%x to 0x%x", call->callref,
+		remote->callref);
+	msg->msg_type = MNCC_DISC_REQ;
+	msg->callref = remote->callref;
+	send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
+	/* sever the end-to-end association */
+	call->other_leg = 0;
+	remote->other_leg = 0;
+}
+
+static void
+handle_release(call, msg)
+	struct gsm_call *call;
+	struct gsm_mncc *msg;
+{
+	struct gsm_call *remote;
+
+	/* do we have a far end? */
+	remote = call->other_leg;
+	/* free the near end */
+	call->gc_flag = 1;
+	/* if no remote, nothing more to do */
+	if (!remote)
+		return;
+	syslog(LOG_DEBUG, "release with remote: from 0x%x to 0x%x",
+		call->callref, remote->callref);
+	/* send them a release request */
+	msg->msg_type = MNCC_REL_REQ;
+	msg->callref = remote->callref;
+	send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
+	/* sever the end-to-end association */
+	remote->other_leg = 0;
+}
+
+void
+internal_switch_mncc(call, msg)
+	struct gsm_call *call;
+	struct gsm_mncc *msg;
+{
+	switch (msg->msg_type) {
+	case MNCC_SETUP_CNF:
+		handle_setup_cnf(call, msg);
+		return;
+	case MNCC_ALERT_IND:
+		forward_to_remote(call, msg, MNCC_ALERT_REQ);
+		return;
+	case MNCC_NOTIFY_IND:
+		forward_to_remote(call, msg, MNCC_NOTIFY_REQ);
+		return;
+	case MNCC_USERINFO_IND:
+		forward_to_remote(call, msg, MNCC_USERINFO_REQ);
+		return;
+	case MNCC_DISC_IND:
+		handle_disconnect(call, msg);
+		return;
+	case MNCC_START_DTMF_IND:
+		msg->msg_type = MNCC_START_DTMF_REJ;
+		mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU,
+				GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
+		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
+		return;
+	case MNCC_STOP_DTMF_IND:
+		msg->msg_type = MNCC_STOP_DTMF_RSP;
+		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
+		return;
+	case MNCC_MODIFY_IND:
+		msg->msg_type = MNCC_MODIFY_REJ;
+		mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU,
+				GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
+		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
+		return;
+	case MNCC_HOLD_IND:
+		msg->msg_type = MNCC_HOLD_REJ;
+		mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU,
+				GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
+		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
+		return;
+	case MNCC_RETRIEVE_IND:
+		msg->msg_type = MNCC_RETRIEVE_REJ;
+		mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU,
+				GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
+		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
+		return;
+	case MNCC_SETUP_COMPL_IND:
+	case MNCC_CALL_CONF_IND:
+		/* no handling needed */
+		return;
+	case MNCC_REL_IND:
+	case MNCC_REJ_IND:
+		handle_release(call, msg);
+		return;
+	case MNCC_REL_CNF:
+		call->gc_flag = 1;
+		return;
+	default:
+		syslog(LOG_ERR, "%s unhandled for internal switch",
+			mncc_msg_name(msg->msg_type));
+	}
+}