diff mgw/gsm2pstn.c @ 103:3b3f07b112f3

mgw: implement GSM to PSTN forwarding
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 26 Sep 2022 20:46:19 -0800
parents
children f062c32a5116
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mgw/gsm2pstn.c	Mon Sep 26 20:46:19 2022 -0800
@@ -0,0 +1,145 @@
+/*
+ * In this module we implement our RTP gateway function
+ * in the GSM to PSTN direction.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.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 "../include/tmgw_ctrl.h"
+#include "../include/tmgw_const.h"
+#include "struct.h"
+#include "select.h"
+#include "int_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;
+	int16_t seq_delta;
+	int32_t ts_delta;
+	int16_t pcm_samples[SAMPLES_PER_FRAME];
+	int rc, m_out;
+
+	addrlen = sizeof(struct sockaddr_in);
+	rc = recvfrom(in_fd, &pkt, sizeof pkt, 0,
+			(struct sockaddr *) &sin_from, &addrlen);
+	if (rc < 0)
+		return;
+	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 (rc != ep->gsm_rtp_pkt_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;
+	if ((pkt.payload[0] & 0xF0) != ep->gsm_payload_magic)
+		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)
+				m_out = 0;
+			else if (ts_delta > 0 &&
+				 ts_delta % SAMPLES_PER_FRAME == 0)
+				m_out = 1;
+			else {
+				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 = 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:
+		gsm_decode(ep->gsm_decoder_state, pkt.payload, pcm_samples);
+		break;
+	}
+	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;
+		break;
+	}
+	select_handlers[ep->rtp_gsm.rtp_fd] = gsm2pstn_rtp_in;
+	return TMGW_RESP_OK;
+}