view sip-in/mncc_handle.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 c1c94b7fc2e2
children 5685412bd6aa
line wrap: on
line source

/*
 * In this module we implement our handling of call control messages
 * from OsmoMSC relayed to us via themwi-mncc.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.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 "call.h"

extern struct call *find_call_by_mncc_callref();

static struct gsm_mncc_cause default_cause = {
	.coding		= GSM48_CAUSE_CODING_GSM,
	.location	= GSM48_CAUSE_LOC_PRN_S_LU,
	.value		= GSM48_CC_CAUSE_NORMAL_UNSPEC,
};

void
send_mncc_connect_ack(call)
	struct call *call;
{
	struct gsm_mncc ack;

	bzero(&ack, sizeof(struct gsm_mncc));
	ack.msg_type = MNCC_SETUP_COMPL_REQ;
	ack.callref = call->mncc_callref;
	send_mncc_to_gsm(&ack, sizeof(struct gsm_mncc));
	call->mncc_state = MNCC_STATE_CONNECTED;
	call->overall_state = OVERALL_STATE_CONNECTED;
}

static void
handle_alerting(call, msg)
	struct call *call;
	struct gsm_mncc *msg;
{
	if (call->mncc_state != MNCC_STATE_STARTED) {
		syslog(LOG_ERR, "MNCC_ALERT_IND in wrong MNCC state 0x%x",
			call->mncc_state);
		return;
	}
	call->mncc_state = MNCC_STATE_ALERTING;
	call->overall_state = OVERALL_STATE_ALERTING;
	signal_invite_ringing(call);
}

static void
handle_answer(call, msg)
	struct call *call;
	struct gsm_mncc *msg;
{
	if (call->mncc_state != MNCC_STATE_STARTED &&
	    call->mncc_state != MNCC_STATE_ALERTING) {
		syslog(LOG_ERR, "MNCC_SETUP_CNF in wrong MNCC state 0x%x",
			call->mncc_state);
		return;
	}
	call->mncc_state = MNCC_STATE_ANSWERED;
	call->overall_state = OVERALL_STATE_ANSWERED;
	/* right now we require MNCC_RTP_CREATE to have come first */
	if (!call->gsm_payload_msg_type) {
		call->overall_state = OVERALL_STATE_TEARDOWN;
		disconnect_mncc(call, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_PROTO_ERR);
		disconnect_tmgw(call);
		strcpy(call->invite_fail, "502 Internal protocol error");
		signal_invite_error(call);
		return;
	}
	tmgw_send_mdcx_connect(call);
}

static void
handle_disconnect_ind(call, msg)
	struct call *call;
	struct gsm_mncc *msg;
{
	struct gsm_mncc_cause *cause;

	/* release back to MNCC */
	msg->msg_type = MNCC_REL_REQ;
	send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
	call->mncc_state = MNCC_STATE_RELEASE;
	/* signal disconnect to SIP */
	call->overall_state = OVERALL_STATE_TEARDOWN;
	if (msg->fields & MNCC_F_CAUSE)
		cause = &msg->cause;
	else
		cause = &default_cause;
	disconnect_sip(call, cause);
	disconnect_tmgw(call);
}

static void
handle_final_release(call, msg)
	struct call *call;
	struct gsm_mncc *msg;
{
	struct gsm_mncc_cause *cause;

	/* MNCC call leg is gone */
	call->mncc_state = MNCC_STATE_NO_EXIST;
	/* signal disconnect to SIP */
	call->overall_state = OVERALL_STATE_TEARDOWN;
	if (msg->fields & MNCC_F_CAUSE)
		cause = &msg->cause;
	else
		cause = &default_cause;
	disconnect_sip(call, cause);
	disconnect_tmgw(call);
	transition_dead_sip(call);
}

static void
handle_signaling_msg(msg, msglen)
	struct gsm_mncc *msg;
	unsigned msglen;
{
	struct call *call;

	if (msglen != sizeof(struct gsm_mncc)) {
		syslog(LOG_CRIT,
			"FATAL: Rx MNCC message type 0x%x has wrong length",
			msg->msg_type);
		exit(1);
	}
	call = find_call_by_mncc_callref(msg->callref);
	if (!call) {
		syslog(LOG_CRIT,
		"error: Rx MNCC message type 0x%x has invalid callref 0x%x",
			msg->msg_type, msg->callref);
		exit(1);
	}
	switch (msg->msg_type) {
	case MNCC_SETUP_CNF:
		handle_answer(call, msg);
		return;
	case MNCC_ALERT_IND:
		handle_alerting(call, msg);
		return;
	case MNCC_DISC_IND:
		handle_disconnect_ind(call, msg);
		return;
	case MNCC_REL_IND:
	case MNCC_REL_CNF:
	case MNCC_REJ_IND:
		handle_final_release(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;
	}
}

static void
handle_rtp_create(msg, msglen)
	struct gsm_mncc_rtp *msg;
	unsigned msglen;
{
	struct call *call;

	if (msglen != sizeof(struct gsm_mncc_rtp)) {
		syslog(LOG_CRIT,
			"FATAL: Rx MNCC message type 0x%x has wrong length",
			msg->msg_type);
		exit(1);
	}
	call = find_call_by_mncc_callref(msg->callref);
	if (!call) {
		syslog(LOG_CRIT,
		"error: Rx MNCC message type 0x%x has invalid callref 0x%x",
			msg->msg_type, msg->callref);
		exit(1);
	}
	/* save Osmocom network RTP information */
	bcopy(&msg->addr, &call->gsm_rtp_osmo, sizeof(struct sockaddr_storage));
	call->gsm_payload_type = msg->payload_type;
	call->gsm_payload_msg_type = msg->payload_msg_type;
	/* respond with MNCC_RTP_CONNECT */
	msg->msg_type = MNCC_RTP_CONNECT;
	bcopy(&call->gsm_rtp_tmgw, &msg->addr, sizeof(struct sockaddr_storage));
	send_mncc_to_gsm(msg, sizeof(struct gsm_mncc_rtp));
}

void
msg_from_mncc(msg, msglen)
	union mncc_msg *msg;
	unsigned msglen;
{
	switch (msg->msg_type) {
	case MNCC_SETUP_CNF:
	case MNCC_CALL_CONF_IND:
	case MNCC_ALERT_IND:
	case MNCC_NOTIFY_IND:
	case MNCC_DISC_IND:
	case MNCC_FACILITY_IND:
	case MNCC_START_DTMF_IND:
	case MNCC_STOP_DTMF_IND:
	case MNCC_MODIFY_IND:
	case MNCC_HOLD_IND:
	case MNCC_RETRIEVE_IND:
	case MNCC_USERINFO_IND:
	case MNCC_REL_IND:
	case MNCC_REL_CNF:
	case MNCC_REJ_IND:
		handle_signaling_msg(msg, msglen);
		return;
	case MNCC_RTP_CREATE:
		handle_rtp_create(msg, msglen);
		return;
	case MNCC_RTP_CONNECT:
		syslog(LOG_ERR, "MNCC_RTP_CONNECT error from OsmoMSC");
		return;
	case MNCC_RTP_FREE:
		syslog(LOG_ERR, "MNCC_RTP_FREE bogon from OsmoMSC");
		return;
	default:
		syslog(LOG_CRIT,
			"FATAL: received unexpected MNCC message type 0x%x",
			msg->msg_type);
		exit(1);
	}
}