view mncc/intswitch.c @ 124:7e04d28fae8b

sip-in: default use-100rel to no BulkVS servers act badly when we send a reliable 180 Ringing response to an incoming call, even though they advertise 100rel support in the Supported header in the INVITE packet, and we probably won't be implementing 100rel for outbound because doing per-the-spec PRACK as a UAC is just too burdensome. Therefore, we need to consider 100rel extension as not-really-supported in themwi-system-sw.
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 01 Oct 2022 15:54:50 -0800
parents 660126bd5f59
children
line wrap: on
line source

/*
 * 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));
	}
}