view sip-out/mncc_sig_in.c @ 198:cf1ba5d65188

mgw: start using project-global rtp_defs.h
author Mychaela Falconia <falcon@freecalypso.org>
date Wed, 29 Mar 2023 20:06:40 -0800
parents e54b0a9e322f
children
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 "../include/out_routes.h"
#include "call.h"

extern struct call *find_call_by_mncc_callref();

static void
handle_disconnect_ind(call, msg)
	struct call *call;
	struct gsm_mncc *msg;
{
	/* release back to MNCC */
	msg->msg_type = MNCC_REL_REQ;
	send_mncc_to_sock(call->mncc, msg, sizeof(struct gsm_mncc));
	call->mncc_state = MNCC_STATE_RELEASE;
	/* signal disconnect to SIP */
	call->overall_state = OVERALL_STATE_TEARDOWN;
	disconnect_sip(call);
	disconnect_tmgw(call);
}

static void
handle_final_release(call, msg)
	struct call *call;
	struct gsm_mncc *msg;
{
	/* MNCC call leg is gone */
	call->mncc = 0;
	/* signal disconnect to SIP */
	call->overall_state = OVERALL_STATE_TEARDOWN;
	disconnect_sip(call);
	disconnect_tmgw(call);
	check_dead_call(call);
}

static void
handle_dtmf_start(call, msg)
	struct call *call;
	struct gsm_mncc *msg;
{
	if (!(msg->fields & MNCC_F_KEYPAD) ||
	    !is_valid_dtmf_digit(msg->keypad)) {
		msg->msg_type = MNCC_START_DTMF_REJ;
		mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_INVAL_MAND_INF);
		send_mncc_to_sock(call->mncc, msg, sizeof(struct gsm_mncc));
		return;
	}
	if (call->mncc_state != MNCC_STATE_CONNECTED ||
	    call->mgw_state != MGW_STATE_COMPLETE) {
		msg->msg_type = MNCC_START_DTMF_REJ;
		mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_MSGTYPE_INCOMPAT);
		send_mncc_to_sock(call->mncc, msg, sizeof(struct gsm_mncc));
		return;
	}
	call->dtmf_digit = msg->keypad;
	tmgw_send_dtmf_start(call);
}

static void
handle_dtmf_stop(call, msg)
	struct call *call;
	struct gsm_mncc *msg;
{
	if (call->mncc_state != MNCC_STATE_CONNECTED)
		return;
	if (call->mgw_state == MGW_STATE_COMPLETE)
		tmgw_send_dtmf_stop(call);
	else if (call->mgw_state == MGW_STATE_DTMF_OP)
		call->dtmf_pending_stop = 1;
	else {
		/* dummy OK response */
		msg->msg_type = MNCC_STOP_DTMF_RSP;
		send_mncc_to_sock(call->mncc, msg, sizeof(struct gsm_mncc));
	}
}

static void
handle_call_hold(call, msg)
	struct call *call;
	struct gsm_mncc *msg;
{
	if (call->mncc_state != MNCC_STATE_CONNECTED ||
	    call->mgw_state != MGW_STATE_COMPLETE) {
		msg->msg_type = MNCC_HOLD_REJ;
		mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_MSGTYPE_INCOMPAT);
		send_mncc_to_sock(call->mncc, msg, sizeof(struct gsm_mncc));
		return;
	}
	tmgw_send_mdcx_hold(call);
}

static void
handle_call_retrieve(call, msg)
	struct call *call;
	struct gsm_mncc *msg;
{
	if (call->mncc_state != MNCC_STATE_CONNECTED ||
	    call->mgw_state != MGW_STATE_HELD) {
		msg->msg_type = MNCC_RETRIEVE_REJ;
		mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_MSGTYPE_INCOMPAT);
		send_mncc_to_sock(call->mncc, msg, sizeof(struct gsm_mncc));
		return;
	}
	send_rtp_connect(call);
	tmgw_send_mdcx_retrieve(call);
}

void
handle_mncc_signal(mncc, msg, msglen)
	struct mncc_conn *mncc;
	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(mncc, 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_DISC_IND:
		handle_disconnect_ind(call, msg);
		return;
	case MNCC_REL_IND:
	case MNCC_REL_CNF:
		handle_final_release(call, msg);
		return;
	case MNCC_START_DTMF_IND:
		handle_dtmf_start(call, msg);
		return;
	case MNCC_STOP_DTMF_IND:
		handle_dtmf_stop(call, msg);
		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_sock(mncc, msg, sizeof(struct gsm_mncc));
		return;
	case MNCC_HOLD_IND:
		handle_call_hold(call, msg);
		return;
	case MNCC_RETRIEVE_IND:
		handle_call_retrieve(call, msg);
		return;
	}
}

void
handle_rtp_create(mncc, msg, msglen)
	struct mncc_conn *mncc;
	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(mncc, 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);
	}
	/* we need to be in the right state */
	if (call->mncc_state != MNCC_STATE_MO_PROC) {
		syslog(LOG_ERR,
		"duplicate MNCC_RTP_CREATE for MO callref 0x%x, ignoring",
			msg->callref);
		return;
	}
	/* 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;
	/* move forward */
	call->mncc_state = MNCC_STATE_GOT_RTP;
	call->overall_state = OVERALL_STATE_CRCX;
	tmgw_send_crcx(call);
}