view mgw/gsm2pstn.c @ 216:6aa2cd650943

tcpserv-dump: specify bind IP on the command line
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 24 Jul 2023 22:19:08 -0800
parents 0047c4c08d9e
children
line wrap: on
line source

/*
 * In this module we implement our RTP gateway function
 * in the GSM to PSTN direction.
 */

#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 <unistd.h>
#include <gsm.h>	/* libgsm dependency */
#include <gsm_fr_preproc.h>
#include <gsm_efr.h>
#include "../include/codec_defs.h"
#include "../include/rtp_defs.h"
#include "../include/tmgw_ctrl.h"
#include "../include/tmgw_const.h"
#include "struct.h"
#include "select.h"
#include "int_defs.h"
#include "dtmf_defs.h"

#define	ERR_WRONG_UDP_SRC	0x0001
#define	ERR_BAD_RTP_PACKET	0x0002
#define	ERR_SSRC_CHANGE		0x0004
#define	ERR_SEQ_BREAK		0x0008
#define	ERR_TSTAMP_BREAK	0x0010

void
gsm2pstn_rtp_in(in_fd, ep)
	struct endpoint *ep;
{
	struct rtp_packet pkt;
	struct sockaddr_in sin_from;
	socklen_t addrlen;
	unsigned pktsize;
	int16_t seq_delta;
	int32_t ts_delta;
	uint8_t codec_frame[GSM_FR_BYTES];
	int16_t pcm_samples[SAMPLES_PER_FRAME];
	int rc, bfi, bfi_nodata, taf, m_out;

	addrlen = sizeof(struct sockaddr_in);
	rc = recvfrom(in_fd, &pkt, sizeof pkt, 0,
			(struct sockaddr *) &sin_from, &addrlen);
	if (rc < 0)
		return;
	pktsize = rc;
	if (sin_from.sin_addr.s_addr != ep->rtp_gsm.remote_addr.sin_addr.s_addr
	    || sin_from.sin_port != ep->rtp_gsm.remote_addr.sin_port) {
		if (!(ep->g2p_err_flags & ERR_WRONG_UDP_SRC)) {
			syslog(LOG_ERR,
				"GSM RTP ep got UDP packet from wrong source");
			ep->g2p_err_flags |= ERR_WRONG_UDP_SRC;
		}
		return;
	}
	if (pktsize < RTP_PACKET_HDR_SIZE) {
bad_rtp_pkt:	if (!(ep->g2p_err_flags & ERR_BAD_RTP_PACKET)) {
			syslog(LOG_ERR, "Rx bad RTP packet on GSM side");
			ep->g2p_err_flags |= ERR_BAD_RTP_PACKET;
		}
		return;
	}
	if (pkt.v_p_x_cc != 0x80)
		goto bad_rtp_pkt;
	if ((pkt.m_pt & 0x7F) != ep->gsm_payload_type)
		goto bad_rtp_pkt;
	pktsize -= RTP_PACKET_HDR_SIZE;
	if (!pktsize) {
		/* zero length payload */
		bfi = bfi_nodata = 1;
		taf = 0;
	} else if (pktsize == ep->gsm_payload_len &&
		   (pkt.payload[0] & 0xF0) == ep->gsm_payload_magic) {
		/* standard RFC 3551 or TS 101 318 payload */
		bfi = bfi_nodata = 0;
		taf = 0;
		bcopy(pkt.payload, codec_frame, pktsize);
	} else if (pktsize == 2 && pkt.payload[0] == 0xBF) {
		/* old BFI marker */
		bfi = bfi_nodata = 1;
		taf = pkt.payload[1] & 1;
	} else if ((pkt.payload[0] & 0xF0) == 0xE0) {
		/* new TRAUlike RTP payload format */
		bfi_nodata = (pkt.payload[0] & 0x04) >> 2;
		bfi = (pkt.payload[0] & 0x02) >> 1;
		taf = pkt.payload[0] & 0x01;
		if (bfi_nodata) {
			if (!bfi)
				goto bad_rtp_pkt;
			if (pktsize != 1)
				goto bad_rtp_pkt;
		} else {
			if (pktsize != ep->gsm_payload_len + 1)
				goto bad_rtp_pkt;
			if ((pkt.payload[1] & 0xF0) != ep->gsm_payload_magic)
				goto bad_rtp_pkt;
			bcopy(pkt.payload+1, codec_frame, ep->gsm_payload_len);
		}
	} else
		goto bad_rtp_pkt;
	if (ep->g2p_state && pkt.ssrc != ep->g2p_ssrc) {
		if (!(ep->g2p_err_flags & ERR_SSRC_CHANGE)) {
			syslog(LOG_ERR, "GSM RTP stream changed SSRC");
			ep->g2p_err_flags |= ERR_SSRC_CHANGE;
		}
		ep->g2p_state = 0;
	}
	if (ep->g2p_state) {
		seq_delta = ntohs(pkt.seq) - ep->g2p_last_seq;
		ts_delta = ntohl(pkt.tstamp) - ep->g2p_last_ts;
		if (seq_delta <= 0)
			return;	/* discard old or duplicate */
		if (seq_delta != 1) {
			if (!(ep->g2p_err_flags & ERR_SEQ_BREAK)) {
				syslog(LOG_ERR, "GSM RTP stream seq break");
				ep->g2p_err_flags |= ERR_SEQ_BREAK;
			}
			m_out = 1;
		} else if (ts_delta != SAMPLES_PER_FRAME) {
			if (!(ep->g2p_err_flags & ERR_TSTAMP_BREAK)) {
				syslog(LOG_ERR, "GSM RTP stream tstamp break");
				ep->g2p_err_flags |= ERR_TSTAMP_BREAK;
			}
			m_out = 1;
		} else
			m_out = 0;
	} else
		m_out = 1;
	ep->g2p_state = 1;
	ep->g2p_ssrc = pkt.ssrc;
	ep->g2p_last_ts = ntohl(pkt.tstamp);
	ep->g2p_last_seq = ntohs(pkt.seq);
	/* actual transcoding and forwarding */
	if (!(ep->fwd_mode & TMGW_FWD_ENABLE_GSM2PSTN)) {
		ep->g2p_drop_flag = 1;
		return;
	}
	if (ep->g2p_drop_flag) {
		ep->g2p_drop_flag = 0;
		m_out = 1;
	}
	switch (ep->gsm_payload_msg_type) {
	case GSM_TCHF_FRAME:
		if (bfi)
			gsmfr_preproc_bfi(ep->gsm_decoder_extra_state, taf,
					  codec_frame);
		else
			gsmfr_preproc_good_frame(ep->gsm_decoder_extra_state,
						 codec_frame);
		gsm_decode(ep->gsm_decoder_state, codec_frame, pcm_samples);
		break;
	case GSM_TCHF_FRAME_EFR:
		if (bfi_nodata)
			EFR_decode_bfi_nodata(ep->gsm_decoder_state, taf,
					      pcm_samples);
		else
			EFR_decode_frame(ep->gsm_decoder_state, codec_frame,
					 bfi, taf, pcm_samples);
		break;
	}
	if (ep->dtmf_sample_ptr) {
		bcopy(ep->dtmf_sample_ptr, pcm_samples, SAMPLES_PER_FRAME*2);
		ep->dtmf_sample_ptr += SAMPLES_PER_FRAME;
		ep->dtmf_frames_sent++;
		if (ep->dtmf_frames_sent >= DTMF_MAX_FRAMES)
			ep->dtmf_sample_ptr = 0;
	}
	pkt.m_pt = ep->pstn_payload_type;
	if (m_out)
		pkt.m_pt |= 0x80;
	pkt.seq = htons(++ep->g2p_out_seq);
	g711_encode_frame(pcm_samples, pkt.payload, ep->pstn_payload_type);
	addrlen = sizeof(struct sockaddr_in);
	sendto(ep->rtp_pstn.rtp_fd, &pkt, RTP_PACKET_SIZE_PSTN, 0,
		(struct sockaddr *) &ep->rtp_pstn.remote_addr, addrlen);
}

gsm2pstn_init(ep)
	struct endpoint *ep;
{
	if (ep->gsm_decoder_state)
		return TMGW_RESP_OK;
	switch (ep->gsm_payload_msg_type) {
	case GSM_TCHF_FRAME:
		ep->gsm_decoder_state = gsm_create();
		if (!ep->gsm_decoder_state)
			return TMGW_RESP_ERR_RSRC;
		ep->gsm_decoder_extra_state = gsmfr_preproc_create();
		if (!ep->gsm_decoder_extra_state) {
			free(ep->gsm_decoder_state);
			ep->gsm_decoder_state = 0;
			return TMGW_RESP_ERR_RSRC;
		}
		break;
	case GSM_TCHF_FRAME_EFR:
		ep->gsm_decoder_state = EFR_decoder_create();
		if (!ep->gsm_decoder_state)
			return TMGW_RESP_ERR_RSRC;
		break;
	}
	select_handlers[ep->rtp_gsm.rtp_fd] = gsm2pstn_rtp_in;
	return TMGW_RESP_OK;
}