mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-11-03 21:43:32 +00:00 
			
		
		
		
	Compare commits
	
		
			18 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					a02faff1ac | ||
| 
						 | 
					ce055d5ac4 | ||
| 
						 | 
					452f2ba5bd | ||
| 
						 | 
					0b6faa4521 | ||
| 
						 | 
					1c69fb0f14 | ||
| 
						 | 
					069dd16b67 | ||
| 
						 | 
					e144275757 | ||
| 
						 | 
					e0058b7207 | ||
| 
						 | 
					bb3ccdea1a | ||
| 
						 | 
					2799ff9bb9 | ||
| 
						 | 
					fed5feafd3 | ||
| 
						 | 
					1de5ed6f97 | ||
| 
						 | 
					80751d850b | ||
| 
						 | 
					2c40164ff0 | ||
| 
						 | 
					e308202285 | ||
| 
						 | 
					b4a067c9fb | ||
| 
						 | 
					8029b12146 | ||
| 
						 | 
					ebb05c1f90 | 
@@ -24,3 +24,4 @@
 | 
			
		||||
# If any interfaces have been removed or changed since the last public release, a=0.
 | 
			
		||||
#
 | 
			
		||||
#library		what		description / commit summary line
 | 
			
		||||
libosmogsm              >1.6.0          use of iuup.h (libosmocore.git Ib21cee2e30bf83dff4e167f79541796007af9845)
 | 
			
		||||
 
 | 
			
		||||
@@ -107,8 +107,8 @@ We are planning to add further endpoint types for:
 | 
			
		||||
 | 
			
		||||
You can find the OsmoMGW issue tracker and wiki online at
 | 
			
		||||
 | 
			
		||||
- https://osmocom.org/projects/osmomgw
 | 
			
		||||
- https://osmocom.org/projects/osmomgw/wiki
 | 
			
		||||
- https://osmocom.org/projects/osmo-mgw
 | 
			
		||||
- https://osmocom.org/projects/osmo-mgw/wiki
 | 
			
		||||
 | 
			
		||||
RFC 3435 for MGCP is located at
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,4 +13,5 @@ noinst_HEADERS = \
 | 
			
		||||
	mgcp_e1.h \
 | 
			
		||||
	mgcp_network.h \
 | 
			
		||||
	mgcp_protocol.h \
 | 
			
		||||
	mgcp_iuup.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -17,3 +17,4 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn);
 | 
			
		||||
int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type);
 | 
			
		||||
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
 | 
			
		||||
								const char *subtype_name, unsigned int match_nr);
 | 
			
		||||
bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec);
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@
 | 
			
		||||
#include <osmocom/mgcp/osmux.h>
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/gsm/iuup.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
#define LOGPCONN(conn, cat, level, fmt, args...) \
 | 
			
		||||
@@ -47,6 +48,7 @@ enum mgcp_conn_rtp_type {
 | 
			
		||||
	MGCP_RTP_DEFAULT	= 0,
 | 
			
		||||
	MGCP_OSMUX_BSC,
 | 
			
		||||
	MGCP_OSMUX_BSC_NAT,
 | 
			
		||||
	MGCP_RTP_IUUP,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! Connection type, specifies which member of the union "u" in mgcp_conn
 | 
			
		||||
@@ -93,6 +95,14 @@ struct mgcp_conn_rtp {
 | 
			
		||||
		} stats;
 | 
			
		||||
	} osmux;
 | 
			
		||||
 | 
			
		||||
	struct {
 | 
			
		||||
		struct osmo_iuup_instance *iui;
 | 
			
		||||
		bool active_init; /* true: Send IuUP Init */
 | 
			
		||||
		int rfci_id_no_data; /* RFCI Id for RFCI NO_DATA (-1 if not available) */
 | 
			
		||||
		bool configured;
 | 
			
		||||
		struct osmo_iuup_rnl_prim *init_ind;
 | 
			
		||||
	} iuup;
 | 
			
		||||
 | 
			
		||||
	struct rate_ctr_group *rate_ctr_group;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -176,6 +186,12 @@ static inline bool mgcp_conn_rtp_is_osmux(const struct mgcp_conn_rtp *conn) {
 | 
			
		||||
	return conn->type == MGCP_OSMUX_BSC || conn->type == MGCP_OSMUX_BSC_NAT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Was conn configured to handle Osmux? */
 | 
			
		||||
static inline bool mgcp_conn_rtp_is_iuup(const struct mgcp_conn_rtp *conn)
 | 
			
		||||
{
 | 
			
		||||
	return conn->type == MGCP_RTP_IUUP;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
 | 
			
		||||
				  enum mgcp_conn_type type, char *name);
 | 
			
		||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								include/osmocom/mgcp/mgcp_iuup.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								include/osmocom/mgcp/mgcp_iuup.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
/* IuUP connection functionalitites */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Pau Espin Pedrol
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
 | 
			
		||||
struct mgcp_conn_rtp;
 | 
			
		||||
 | 
			
		||||
int mgcp_conn_iuup_init(struct mgcp_conn_rtp *conn_rtp);
 | 
			
		||||
void mgcp_conn_iuup_cleanup(struct mgcp_conn_rtp *conn_rtp);
 | 
			
		||||
int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg);
 | 
			
		||||
int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn_rtp *conn_dest_rtp, struct msgb *msg);
 | 
			
		||||
int mgcp_conn_iuup_send_dummy(struct mgcp_conn_rtp *conn_rtp);
 | 
			
		||||
@@ -70,8 +70,6 @@ struct mgcp_rtp_state {
 | 
			
		||||
	 * data is just re-used) */
 | 
			
		||||
	uint16_t alt_rtp_tx_sequence;
 | 
			
		||||
	uint32_t alt_rtp_tx_ssrc;
 | 
			
		||||
 | 
			
		||||
	bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_rtp_codec {
 | 
			
		||||
@@ -110,8 +108,8 @@ struct mgcp_rtp_end {
 | 
			
		||||
	uint32_t packet_duration_ms;
 | 
			
		||||
	int maximum_packet_time; /* -1: not set */
 | 
			
		||||
	char *fmtp_extra;
 | 
			
		||||
	/* are we transmitting packets (1) or dropping (0) outbound packets */
 | 
			
		||||
	int output_enabled;
 | 
			
		||||
	/* are we transmitting packets (true) or dropping (false) outbound packets */
 | 
			
		||||
	bool output_enabled;
 | 
			
		||||
	/* FIXME: This parameter can be set + printed, but is nowhere used! */
 | 
			
		||||
	int force_output_ptime;
 | 
			
		||||
 | 
			
		||||
@@ -177,3 +175,8 @@ void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
 | 
			
		||||
void mgcp_rtp_annex_count(const struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
 | 
			
		||||
			const uint16_t seq, const int32_t transit,
 | 
			
		||||
			const uint32_t ssrc, const bool marker_bit);
 | 
			
		||||
 | 
			
		||||
void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
 | 
			
		||||
				 int id, int inc);
 | 
			
		||||
void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg);
 | 
			
		||||
uint32_t mgcp_get_current_ts(unsigned codec_rate);
 | 
			
		||||
 
 | 
			
		||||
@@ -49,6 +49,7 @@ enum mgcp_codecs {
 | 
			
		||||
	CODEC_GSMHR_8000_1 = 111,
 | 
			
		||||
	CODEC_AMR_8000_1 = 112,
 | 
			
		||||
	CODEC_AMRWB_16000_1 = 113,
 | 
			
		||||
	CODEC_IUFP = 96,
 | 
			
		||||
};
 | 
			
		||||
/* Note: when new codec types are added, the corresponding value strings
 | 
			
		||||
 * in mgcp_client.c (codec_table) must be updated as well. Enumerations
 | 
			
		||||
 
 | 
			
		||||
@@ -1,74 +0,0 @@
 | 
			
		||||
# ===========================================================================
 | 
			
		||||
#   http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
 | 
			
		||||
# ===========================================================================
 | 
			
		||||
#
 | 
			
		||||
# SYNOPSIS
 | 
			
		||||
#
 | 
			
		||||
#   AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
 | 
			
		||||
#
 | 
			
		||||
# DESCRIPTION
 | 
			
		||||
#
 | 
			
		||||
#   Check whether the given FLAG works with the current language's compiler
 | 
			
		||||
#   or gives an error.  (Warnings, however, are ignored)
 | 
			
		||||
#
 | 
			
		||||
#   ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
 | 
			
		||||
#   success/failure.
 | 
			
		||||
#
 | 
			
		||||
#   If EXTRA-FLAGS is defined, it is added to the current language's default
 | 
			
		||||
#   flags (e.g. CFLAGS) when the check is done.  The check is thus made with
 | 
			
		||||
#   the flags: "CFLAGS EXTRA-FLAGS FLAG".  This can for example be used to
 | 
			
		||||
#   force the compiler to issue an error when a bad flag is given.
 | 
			
		||||
#
 | 
			
		||||
#   INPUT gives an alternative input source to AC_COMPILE_IFELSE.
 | 
			
		||||
#
 | 
			
		||||
#   NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
 | 
			
		||||
#   macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
 | 
			
		||||
#
 | 
			
		||||
# LICENSE
 | 
			
		||||
#
 | 
			
		||||
#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
 | 
			
		||||
#   Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
 | 
			
		||||
#
 | 
			
		||||
#   This program is free software: you can redistribute it and/or modify it
 | 
			
		||||
#   under the terms of the GNU General Public License as published by the
 | 
			
		||||
#   Free Software Foundation, either version 3 of the License, or (at your
 | 
			
		||||
#   option) any later version.
 | 
			
		||||
#
 | 
			
		||||
#   This program is distributed in the hope that it will be useful, but
 | 
			
		||||
#   WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 | 
			
		||||
#   Public License for more details.
 | 
			
		||||
#
 | 
			
		||||
#   You should have received a copy of the GNU General Public License along
 | 
			
		||||
#   with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
#
 | 
			
		||||
#   As a special exception, the respective Autoconf Macro's copyright owner
 | 
			
		||||
#   gives unlimited permission to copy, distribute and modify the configure
 | 
			
		||||
#   scripts that are the output of Autoconf when processing the Macro. You
 | 
			
		||||
#   need not follow the terms of the GNU General Public License when using
 | 
			
		||||
#   or distributing such scripts, even though portions of the text of the
 | 
			
		||||
#   Macro appear in them. The GNU General Public License (GPL) does govern
 | 
			
		||||
#   all other use of the material that constitutes the Autoconf Macro.
 | 
			
		||||
#
 | 
			
		||||
#   This special exception to the GPL applies to versions of the Autoconf
 | 
			
		||||
#   Macro released by the Autoconf Archive. When you make and distribute a
 | 
			
		||||
#   modified version of the Autoconf Macro, you may extend this special
 | 
			
		||||
#   exception to the GPL to apply to your modified version as well.
 | 
			
		||||
 | 
			
		||||
#serial 4
 | 
			
		||||
 | 
			
		||||
AC_DEFUN([AX_CHECK_COMPILE_FLAG],
 | 
			
		||||
[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
 | 
			
		||||
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
 | 
			
		||||
AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
 | 
			
		||||
  ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
 | 
			
		||||
  _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
 | 
			
		||||
  AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
 | 
			
		||||
    [AS_VAR_SET(CACHEVAR,[yes])],
 | 
			
		||||
    [AS_VAR_SET(CACHEVAR,[no])])
 | 
			
		||||
  _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
 | 
			
		||||
AS_VAR_IF(CACHEVAR,yes,
 | 
			
		||||
  [m4_default([$2], :)],
 | 
			
		||||
  [m4_default([$3], :)])
 | 
			
		||||
AS_VAR_POPDEF([CACHEVAR])dnl
 | 
			
		||||
])dnl AX_CHECK_COMPILE_FLAGS
 | 
			
		||||
@@ -59,6 +59,7 @@ const struct value_string osmo_mgcpc_codec_names[] = {
 | 
			
		||||
	{ CODEC_GSMHR_8000_1, "GSM-HR-08/8000/1" },
 | 
			
		||||
	{ CODEC_AMR_8000_1, "AMR/8000/1" },
 | 
			
		||||
	{ CODEC_AMRWB_16000_1, "AMR-WB/16000/1" },
 | 
			
		||||
	{ CODEC_IUFP, "VND.3GPP.IUFP/16000" },
 | 
			
		||||
	{ 0, NULL },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -48,4 +48,5 @@ libosmo_mgcp_a_SOURCES = \
 | 
			
		||||
	mgcp_ctrl.c \
 | 
			
		||||
	mgcp_ratectr.c \
 | 
			
		||||
	mgcp_e1.c \
 | 
			
		||||
	mgcp_iuup.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -355,7 +355,7 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
 | 
			
		||||
 *
 | 
			
		||||
 * https://tools.ietf.org/html/rfc4867
 | 
			
		||||
 */
 | 
			
		||||
static bool amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
 | 
			
		||||
bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	if (!codec->param_present)
 | 
			
		||||
		return false;
 | 
			
		||||
@@ -378,10 +378,10 @@ static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *c
 | 
			
		||||
		return false;
 | 
			
		||||
	if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
 | 
			
		||||
		return false;
 | 
			
		||||
	if (!strcmp(codec_a->subtype_name, "AMR")) {
 | 
			
		||||
		if (amr_is_octet_aligned(codec_a) != amr_is_octet_aligned(codec_b))
 | 
			
		||||
			return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Note: AMR allows to set the RTP payload format to octet-aligned or bandwith-efficient (octet-aligned=0)
 | 
			
		||||
	 * via SDP. This difference concerns payload format only, but not the actual codec. It is not a difference
 | 
			
		||||
	 * within the meaning of this function. */
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
@@ -417,7 +417,7 @@ int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp
 | 
			
		||||
	if (!codec_src)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* Use the codec infrmation from the source and try to find the
 | 
			
		||||
	/* Use the codec information from the source and try to find the
 | 
			
		||||
	 * equivalent of it on the destination side */
 | 
			
		||||
	codecs_assigned = rtp_dst->codecs_assigned;
 | 
			
		||||
	OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,8 @@
 | 
			
		||||
#include <osmocom/mgcp/mgcp_trunk.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_sdp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_codec.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_iuup.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsm/gsm_utils.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
@@ -108,7 +110,7 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *
 | 
			
		||||
	/* Set default values */
 | 
			
		||||
	end->frames_per_packet = 0;	/* unknown */
 | 
			
		||||
	end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
 | 
			
		||||
	end->output_enabled = 0;
 | 
			
		||||
	end->output_enabled = false;
 | 
			
		||||
	end->maximum_packet_time = -1;
 | 
			
		||||
 | 
			
		||||
	conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index++);
 | 
			
		||||
@@ -129,6 +131,8 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
 | 
			
		||||
{
 | 
			
		||||
	if (mgcp_conn_rtp_is_osmux(conn_rtp))
 | 
			
		||||
		conn_osmux_disable(conn_rtp);
 | 
			
		||||
	if (mgcp_conn_rtp_is_iuup(conn_rtp))
 | 
			
		||||
		mgcp_conn_iuup_cleanup(conn_rtp);
 | 
			
		||||
	mgcp_free_rtp_port(&conn_rtp->end);
 | 
			
		||||
	rate_ctr_group_free(conn_rtp->rate_ctr_group);
 | 
			
		||||
	mgcp_codec_reset_all(conn_rtp);
 | 
			
		||||
 
 | 
			
		||||
@@ -338,6 +338,7 @@ static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
 | 
			
		||||
	trunk = mgcp_trunk_by_line_num(cfg, ts->line->num);
 | 
			
		||||
	if (!trunk) {
 | 
			
		||||
		LOGP(DE1, LOGL_ERROR, "E1-RX: unable to find a trunk for E1-line %u!\n", ts->line->num);
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -359,6 +360,9 @@ static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
 | 
			
		||||
 | 
			
		||||
	/* Trigger sending of pending E1 traffic */
 | 
			
		||||
	e1_send(ts, trunk);
 | 
			
		||||
 | 
			
		||||
	/* e1inp_rx_ts() does not free() msgb */
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										745
									
								
								src/libosmo-mgcp/mgcp_iuup.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										745
									
								
								src/libosmo-mgcp/mgcp_iuup.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,745 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All rights not specifically granted under this license are reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Pau Espin Pedrol
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 * under the terms of the GNU Affero General Public License as published by the
 | 
			
		||||
 * Free Software Foundation; either version 3 of the License, or (at your
 | 
			
		||||
 * option) any later version.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/byteswap.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsm/iuup.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/netif/rtp.h>
 | 
			
		||||
#include <osmocom/netif/amr.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_iuup.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_codec.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_network.h>
 | 
			
		||||
#include <osmocom/mgcp/debug.h>
 | 
			
		||||
 | 
			
		||||
#define MGW_IUUP_MSGB_SIZE 4096
 | 
			
		||||
 | 
			
		||||
static const struct osmo_iuup_rnl_config def_configure_req = {
 | 
			
		||||
	.transparent = false,
 | 
			
		||||
	.active = true,
 | 
			
		||||
	.supported_versions_mask = 0x0003,
 | 
			
		||||
	.num_rfci = 0,
 | 
			
		||||
	.num_subflows = 0,
 | 
			
		||||
	.IPTIs_present = false,
 | 
			
		||||
	.t_init = { .t_ms = IUUP_TIMER_INIT_T_DEFAULT, .n_max = IUUP_TIMER_INIT_N_DEFAULT },
 | 
			
		||||
	.t_ta = { .t_ms = IUUP_TIMER_TA_T_DEFAULT, .n_max = IUUP_TIMER_TA_N_DEFAULT },
 | 
			
		||||
	.t_rc = { .t_ms = IUUP_TIMER_RC_T_DEFAULT, .n_max = IUUP_TIMER_RC_N_DEFAULT },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Find a destination connection. */
 | 
			
		||||
static struct mgcp_conn *_find_dst_conn(struct mgcp_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	/* NOTE: This code path runs every time an RTP packet is received. The
 | 
			
		||||
	 * function mgcp_find_dst_conn() we use to determine the detination
 | 
			
		||||
	 * connection will iterate the connection list inside the endpoint.
 | 
			
		||||
	 * Since list iterations are quite costly, we will figure out the
 | 
			
		||||
	 * destination only once and use the optional private data pointer of
 | 
			
		||||
	 * the connection to cache the destination connection pointer. */
 | 
			
		||||
 | 
			
		||||
	struct mgcp_conn *conn_dst;
 | 
			
		||||
	if (!conn->priv) {
 | 
			
		||||
		conn_dst = mgcp_find_dst_conn(conn);
 | 
			
		||||
		conn->priv = conn_dst;
 | 
			
		||||
	} else {
 | 
			
		||||
		conn_dst = (struct mgcp_conn *)conn->priv;
 | 
			
		||||
	}
 | 
			
		||||
	return conn_dst;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Find RFCI containing all 0 sizes, -1 if not found. irp is an Initialization.ind prim */
 | 
			
		||||
static int _find_rfci_no_data(struct osmo_iuup_rnl_prim *irp)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	uint8_t rfci_cnt = 0;
 | 
			
		||||
	/* Find RFCI containing NO_DATA: */
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(irp->u.status.u.initialization.rfci); i++) {
 | 
			
		||||
		struct osmo_iuup_rfci *rfci = &irp->u.status.u.initialization.rfci[i];
 | 
			
		||||
		int j;
 | 
			
		||||
		bool is_no_data;
 | 
			
		||||
		if (!rfci->used)
 | 
			
		||||
			continue;
 | 
			
		||||
		rfci_cnt++;
 | 
			
		||||
 | 
			
		||||
		is_no_data = true;
 | 
			
		||||
		for (j = 0; j < irp->u.status.u.initialization.num_subflows; j++) {
 | 
			
		||||
			if (rfci->subflow_sizes[j]) {
 | 
			
		||||
				is_no_data = false;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (is_no_data)
 | 
			
		||||
			return rfci->id;
 | 
			
		||||
 | 
			
		||||
		/* early loop termination: */
 | 
			
		||||
		if (rfci_cnt == irp->u.status.u.initialization.num_subflows)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Lookup RFCI to use for specific AMR codec type. -1 if none found */
 | 
			
		||||
static int8_t _conn_iuup_amr_ft_2_rfci(struct mgcp_conn_rtp *conn_rtp, uint8_t ft)
 | 
			
		||||
{
 | 
			
		||||
	int8_t i;
 | 
			
		||||
	uint8_t rfci_cnt = 0;
 | 
			
		||||
	unsigned match_bytes = (unsigned)osmo_amr_bytes(ft);
 | 
			
		||||
	struct osmo_iuup_rnl_prim *irp = conn_rtp->iuup.init_ind;
 | 
			
		||||
	OSMO_ASSERT(irp);
 | 
			
		||||
 | 
			
		||||
	/* TODO: cache this somehow */
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(irp->u.status.u.initialization.rfci); i++) {
 | 
			
		||||
		struct osmo_iuup_rfci *rfci = &irp->u.status.u.initialization.rfci[i];
 | 
			
		||||
		int j;
 | 
			
		||||
		unsigned num_bits;
 | 
			
		||||
		if (!rfci->used)
 | 
			
		||||
			continue;
 | 
			
		||||
		rfci_cnt++;
 | 
			
		||||
 | 
			
		||||
		num_bits = 0;
 | 
			
		||||
		for (j = 0; j < irp->u.status.u.initialization.num_subflows; j++)
 | 
			
		||||
			num_bits += rfci->subflow_sizes[j];
 | 
			
		||||
		if (match_bytes == (num_bits + 7)/8)
 | 
			
		||||
			return rfci->id;
 | 
			
		||||
 | 
			
		||||
		/* early loop termination: */
 | 
			
		||||
		if (rfci_cnt == irp->u.status.u.initialization.num_subflows)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Helper function to configure IuUP layer FSM as Init-Passive, based on default config */
 | 
			
		||||
static int _conn_iuup_configure_as_passive(struct mgcp_conn_rtp *conn_rtp)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_iuup_rnl_prim *irp;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	conn_rtp->iuup.active_init = false;
 | 
			
		||||
 | 
			
		||||
	/* Tx CONFIG.req */
 | 
			
		||||
	irp = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE);
 | 
			
		||||
	irp->u.config = def_configure_req;
 | 
			
		||||
	irp->u.config.active = conn_rtp->iuup.active_init;
 | 
			
		||||
	if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp)) == 0)
 | 
			
		||||
		conn_rtp->iuup.configured = true;
 | 
			
		||||
	else
 | 
			
		||||
		LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed configuring IuUP layer\n");
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Helper function to configure IuUP layer FSM as Init-Active, based on received
 | 
			
		||||
 * RNL Status-Init primitive from the sister IuUP connection we will bridge to. */
 | 
			
		||||
static int _conn_iuup_configure_as_active(struct mgcp_conn_rtp *conn_rtp, struct osmo_iuup_rnl_prim *init_ind)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_iuup_rnl_prim *irp = init_ind;
 | 
			
		||||
	struct osmo_iuup_rnl_prim *irp2;
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	bool prev_output_enabled;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	conn_rtp->iuup.active_init = true;
 | 
			
		||||
 | 
			
		||||
	/* Find RFCI containing NO_DATA: */
 | 
			
		||||
	conn_rtp->iuup.rfci_id_no_data = _find_rfci_no_data(init_ind);
 | 
			
		||||
 | 
			
		||||
	/* Copy over the rfci_id_no_data, since we reuse the same subflow set: */
 | 
			
		||||
	msg = msgb_copy_c(conn_rtp->conn, irp->oph.msg, "iuup-init-copy");
 | 
			
		||||
	conn_rtp->iuup.init_ind = (struct osmo_iuup_rnl_prim *)msgb_data(msg);
 | 
			
		||||
	conn_rtp->iuup.init_ind->oph.msg = msg;
 | 
			
		||||
 | 
			
		||||
	/* Tx CONFIG.req */
 | 
			
		||||
	irp2 = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE);
 | 
			
		||||
	irp2->u.config.transparent = false;
 | 
			
		||||
	irp2->u.config.active = conn_rtp->iuup.active_init;
 | 
			
		||||
	irp2->u.config.data_pdu_type = irp->u.status.u.initialization.data_pdu_type;
 | 
			
		||||
	irp2->u.config.supported_versions_mask = def_configure_req.supported_versions_mask;
 | 
			
		||||
	irp2->u.config.num_rfci = irp->u.status.u.initialization.num_rfci;
 | 
			
		||||
	irp2->u.config.num_subflows = irp->u.status.u.initialization.num_subflows;
 | 
			
		||||
	irp2->u.config.IPTIs_present = irp->u.status.u.initialization.IPTIs_present;
 | 
			
		||||
	memcpy(irp2->u.config.rfci, irp->u.status.u.initialization.rfci, sizeof(irp2->u.config.rfci));
 | 
			
		||||
	irp2->u.config.t_init = def_configure_req.t_init;
 | 
			
		||||
	irp2->u.config.t_ta = def_configure_req.t_ta;
 | 
			
		||||
	irp2->u.config.t_rc = def_configure_req.t_rc;
 | 
			
		||||
 | 
			
		||||
	/* We need to force allowance of RTP containing Init-ACK back: */
 | 
			
		||||
	prev_output_enabled = conn_rtp->end.output_enabled;
 | 
			
		||||
	conn_rtp->end.output_enabled = true;
 | 
			
		||||
 | 
			
		||||
	if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp2)) == 0)
 | 
			
		||||
		conn_rtp->iuup.configured = true;
 | 
			
		||||
	else
 | 
			
		||||
		LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed configuring IuUP layer\n");
 | 
			
		||||
 | 
			
		||||
	conn_rtp->end.output_enabled = prev_output_enabled;
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Helper function to push an RTP+IuUP pkt up to the IuUP layer FSM through the
 | 
			
		||||
 * TNL primitive interface. */
 | 
			
		||||
static int _conn_iuup_rtp_pl_up(struct mgcp_conn_rtp *conn_rtp, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	/* Send RTP payload (IuUP) up the stack: */
 | 
			
		||||
	struct osmo_iuup_tnl_prim *itp;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	msg->l2h = msgb_data(msg) + sizeof(struct rtp_hdr);
 | 
			
		||||
 | 
			
		||||
	itp = osmo_iuup_tnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, MGW_IUUP_MSGB_SIZE);
 | 
			
		||||
	itp->oph.msg->l2h = msgb_put(itp->oph.msg, msgb_l2len(msg));
 | 
			
		||||
	memcpy(itp->oph.msg->l2h, msgb_l2(msg), msgb_l2len(msg));
 | 
			
		||||
	if ((rc = osmo_iuup_tnl_prim_up(conn_rtp->iuup.iui, itp)) != 0) {
 | 
			
		||||
		LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed passing IuUP-Init to IuUP layer\n");
 | 
			
		||||
	}
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int check_rtp_iuup(const struct mgcp_conn_rtp *conn_rtp, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	size_t min_size = sizeof(struct rtp_hdr);
 | 
			
		||||
	/* Check there's at least 2 bytes of RTP payload (IuUP header). This is
 | 
			
		||||
	 ** mainly to avoid 0-byte payload copy cases */
 | 
			
		||||
	if (msgb_length(msg) < sizeof(struct rtp_hdr) + 2) {
 | 
			
		||||
		LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "RTP-IuUP packet too short (%u < %zu)\n",
 | 
			
		||||
			     msgb_length(msg), min_size);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Bridge received IuUP packet in conn_rtp_src to conn_rtp_dst, an IuUP sister
 | 
			
		||||
 * conn in the endpoint. The function takes ownsership of the irp */
 | 
			
		||||
static int bridge_iuup_to_iuup_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mgcp_conn_rtp *conn_rtp_dst, struct osmo_iuup_rnl_prim *irp)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	/* If we are not configured and we received bridged data, it means
 | 
			
		||||
	 * conn_rtp_src is already configured and INITed, and we can infer
 | 
			
		||||
	 * conn_rtp_src is Init-passive (RNC side), so conn_rtp_dst needs to be
 | 
			
		||||
	 * configured as INIT-active: */
 | 
			
		||||
	if (!conn_rtp_dst->iuup.configured) {
 | 
			
		||||
		OSMO_ASSERT(conn_rtp_src->iuup.init_ind);
 | 
			
		||||
		rc = _conn_iuup_configure_as_active(conn_rtp_dst, conn_rtp_src->iuup.init_ind);
 | 
			
		||||
		if (rc < 0) {
 | 
			
		||||
			msgb_free(irp->oph.msg);
 | 
			
		||||
			return rc;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* We simply forward the msg, without freeing it: */
 | 
			
		||||
	talloc_steal(conn_rtp_dst->conn, irp->oph.msg);
 | 
			
		||||
	irp->oph.operation = PRIM_OP_REQUEST;
 | 
			
		||||
	if ((rc = osmo_iuup_rnl_prim_down(conn_rtp_dst->iuup.iui, irp)) != 0)
 | 
			
		||||
		LOG_CONN_RTP(conn_rtp_dst, LOGL_ERROR, "Failed Tx data down to IuUP layer\n");
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Bridge received IuUP packet in conn_rtp_src to conn_rtp_dst, an RTP (no IuUP)
 | 
			
		||||
 * sister conn in the endpoint. The function takes ownsership of the irp */
 | 
			
		||||
static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mgcp_conn_rtp *conn_rtp_dst, struct osmo_iuup_rnl_prim *irp)
 | 
			
		||||
{
 | 
			
		||||
	/* FIXME: We probably need transcoding here?! Or at least look up AMR modes and translate to related RFCI */
 | 
			
		||||
	uint8_t frame_nr = irp->u.data.frame_nr;
 | 
			
		||||
	uint8_t fqc = irp->u.data.fqc;
 | 
			
		||||
	struct msgb *msg = irp->oph.msg;
 | 
			
		||||
	ssize_t amr_length = 0;
 | 
			
		||||
	int ft;
 | 
			
		||||
	uint8_t *amr_data;
 | 
			
		||||
	struct rtp_hdr *rtp_hdr;
 | 
			
		||||
	struct amr_hdr *amr_hdr;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	ft = osmo_amr_bytes_to_ft(msgb_l3len(msg));
 | 
			
		||||
	if (ft < 0) {
 | 
			
		||||
		LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR,
 | 
			
		||||
			 "Unknown AMR format for size %u\n", msgb_l3len(msg));
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return ft;
 | 
			
		||||
	}
 | 
			
		||||
	msgb_pull_to_l3(msg);
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR: ft %d, len %d\n", ft, msgb_l3len(msg));
 | 
			
		||||
 | 
			
		||||
	if (mgcp_codec_amr_is_octet_aligned(conn_rtp_dst->end.codec)) {
 | 
			
		||||
		amr_hdr = (struct amr_hdr *) msgb_push(msg, sizeof(struct amr_hdr));
 | 
			
		||||
		amr_hdr->cmr = 15; /* no change */
 | 
			
		||||
		amr_hdr->f = 0;
 | 
			
		||||
		amr_hdr->q = !fqc;
 | 
			
		||||
		amr_hdr->ft = ft & 0xff;
 | 
			
		||||
		amr_hdr->pad1 = 0;
 | 
			
		||||
		amr_hdr->pad2 = 0;
 | 
			
		||||
	} else {
 | 
			
		||||
		OSMO_ASSERT(msgb_tailroom(msg) >= 2);
 | 
			
		||||
		msgb_put(msg, 2);
 | 
			
		||||
		osmo_amr_iuup_to_bwe(msgb_data(msg), msgb_length(msg) - 2, msgb_length(msg) + 2);
 | 
			
		||||
		/* fill bwe header */
 | 
			
		||||
		amr_data = msgb_data(msg);
 | 
			
		||||
		/* CMR no change      | follow bit | ft (3 of 4 bits) */
 | 
			
		||||
		amr_data[0] = 15 << 4 | (0 << 3) | (ft >> 1);
 | 
			
		||||
		amr_data[1] |= ((ft & 0x1) << 7) | (((!fqc) & 0x1) << 6);
 | 
			
		||||
		amr_length = (osmo_amr_bits(ft) + 10 + 7) / 8;
 | 
			
		||||
		msgb_trim(msg, amr_length);
 | 
			
		||||
	}
 | 
			
		||||
	rtp_hdr = (struct rtp_hdr *) msgb_push(msg, sizeof(*rtp_hdr));
 | 
			
		||||
	*rtp_hdr = (struct rtp_hdr){
 | 
			
		||||
		.csrc_count = 0,
 | 
			
		||||
		.extension = 0,
 | 
			
		||||
		.padding = 0,
 | 
			
		||||
		.version = 0,
 | 
			
		||||
		.payload_type = conn_rtp_dst->end.codec->payload_type,
 | 
			
		||||
		.marker = 0,
 | 
			
		||||
		.sequence = frame_nr,
 | 
			
		||||
		.timestamp = 0,
 | 
			
		||||
		.ssrc = 0
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	rc = mgcp_send(conn_rtp_dst->conn->endp, true, NULL, msg, conn_rtp_src, conn_rtp_dst);
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle RNL Data primitive received from the IuUP layer FSM: Bridge it to the
 | 
			
		||||
 * sister connection in the endpoint: */
 | 
			
		||||
static int _conn_iuup_rx_rnl_data(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn_dst;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_rtp_dst;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	conn_dst = _find_dst_conn(conn_rtp_src->conn);
 | 
			
		||||
 | 
			
		||||
	/* There is no destination conn, stop here */
 | 
			
		||||
	if (!conn_dst) {
 | 
			
		||||
		LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_DEBUG,
 | 
			
		||||
			 "no connection to forward an incoming IuUP payload to\n");
 | 
			
		||||
		rc = -1;
 | 
			
		||||
		goto free_ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* The destination conn is not an RTP/IuUP connection */
 | 
			
		||||
	if (conn_dst->type != MGCP_CONN_TYPE_RTP) {
 | 
			
		||||
		LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR,
 | 
			
		||||
			 "unable to find suitable destination conn\n");
 | 
			
		||||
		 rc = -1;
 | 
			
		||||
		goto free_ret;
 | 
			
		||||
	}
 | 
			
		||||
	conn_rtp_dst = &conn_dst->u.rtp;
 | 
			
		||||
 | 
			
		||||
	switch (conn_rtp_dst->type) {
 | 
			
		||||
	case MGCP_RTP_IUUP:
 | 
			
		||||
		return bridge_iuup_to_iuup_peer(conn_rtp_src, conn_rtp_dst, irp);
 | 
			
		||||
	case MGCP_RTP_DEFAULT:
 | 
			
		||||
		return bridge_iuup_to_rtp_peer(conn_rtp_src, conn_rtp_dst, irp);
 | 
			
		||||
	case MGCP_OSMUX_BSC:
 | 
			
		||||
	case MGCP_OSMUX_BSC_NAT:
 | 
			
		||||
	default:
 | 
			
		||||
		LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR,
 | 
			
		||||
			 "Forward of IuUP payload to RTP connection type %u not supported!\n",
 | 
			
		||||
			 conn_rtp_dst->type);
 | 
			
		||||
		rc = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
free_ret:
 | 
			
		||||
	msgb_free(irp->oph.msg);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle RNL Status-Init primitive received from the IuUP layer FSM.
 | 
			
		||||
 * Potentially configure sister conn as IuUP Init-Active: */
 | 
			
		||||
static int _conn_iuup_rx_rnl_status_init(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn_dst;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_rtp_dst;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
 | 
			
		||||
	if (conn_rtp_src->iuup.init_ind) {
 | 
			
		||||
		/* We received more than one IuUP Initialization. It's probably
 | 
			
		||||
		 * a retransmission, so simply ignore it (lower layers take care
 | 
			
		||||
		 * of ACKing it). */
 | 
			
		||||
		LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_INFO,
 | 
			
		||||
		  "Ignoring potential IuUP Initialization retrans\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	msg = msgb_copy_c(conn_rtp_src->conn, irp->oph.msg, "iuup-init-copy");
 | 
			
		||||
	conn_rtp_src->iuup.init_ind = (struct osmo_iuup_rnl_prim *)msgb_data(msg);
 | 
			
		||||
	conn_rtp_src->iuup.init_ind->oph.msg = msg;
 | 
			
		||||
 | 
			
		||||
	/* Find RFCI containing NO_DATA: */
 | 
			
		||||
	conn_rtp_src->iuup.rfci_id_no_data = _find_rfci_no_data(irp);
 | 
			
		||||
 | 
			
		||||
	conn_dst = _find_dst_conn(conn_rtp_src->conn);
 | 
			
		||||
	/* If not yet there, peer will potentially be IuUP-Initialized later
 | 
			
		||||
	 * when we attempt to bridge audio towards it. See bridge_iuup_to_iuup_peer() */
 | 
			
		||||
	if (!conn_dst)
 | 
			
		||||
		return 0;
 | 
			
		||||
	conn_rtp_dst = &conn_dst->u.rtp;
 | 
			
		||||
	if (!mgcp_conn_rtp_is_iuup(conn_rtp_dst))
 | 
			
		||||
		return 0; /* Nothing to do */
 | 
			
		||||
 | 
			
		||||
	/* We received IuUP parameters on the peer (RNC), Init actively this conn (against CN): */
 | 
			
		||||
	if (!conn_rtp_dst->iuup.configured)
 | 
			
		||||
		rc = _conn_iuup_configure_as_active(conn_rtp_dst, irp);
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle RNL Status primitives received from the IuUP layer FSM: */
 | 
			
		||||
static int _conn_iuup_rx_rnl_status(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	switch (irp->u.status.procedure) {
 | 
			
		||||
	case IUUP_PROC_INIT:
 | 
			
		||||
		rc = _conn_iuup_rx_rnl_status_init(conn_rtp_src, irp);
 | 
			
		||||
		break;
 | 
			
		||||
	case IUUP_PROC_RATE_CTRL:
 | 
			
		||||
	case IUUP_PROC_TIME_ALIGN:
 | 
			
		||||
	case IUUP_PROC_ERR_EVENT:
 | 
			
		||||
	default:
 | 
			
		||||
		LOG_CONN_RTP(conn_rtp_src, LOGL_ERROR,
 | 
			
		||||
			     "Received IuUP RNL STATUS procedure type %u not handled\n",
 | 
			
		||||
			     irp->u.status.procedure);
 | 
			
		||||
		rc = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Received RNL primitive from the IuUP layer FSM containing IuUP Status or
 | 
			
		||||
 * data. Continue pushing it up the stack, either IuUP Status or Data: */
 | 
			
		||||
static int _conn_iuup_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn_rtp *conn_rtp_src = ctx;
 | 
			
		||||
	struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
 | 
			
		||||
	struct msgb *msg = oph->msg;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	switch (OSMO_PRIM_HDR(&irp->oph)) {
 | 
			
		||||
	case OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION):
 | 
			
		||||
		/* we pass ownsership of msg here: */
 | 
			
		||||
		rc = _conn_iuup_rx_rnl_data(conn_rtp_src, irp);
 | 
			
		||||
		break;
 | 
			
		||||
	case OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION):
 | 
			
		||||
		rc = _conn_iuup_rx_rnl_status(conn_rtp_src, irp);
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		OSMO_ASSERT(false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Send |RTP+IuUP| data down the stack of the specified destination connection.
 | 
			
		||||
 *  \param[in] endp associated endpoint (for configuration, logging).
 | 
			
		||||
 *  \param[in] buf buffer that contains the |RTP+IuUP| data.
 | 
			
		||||
 *  \param[in] len length of the buffer that contains the |RTP+IuUP| data.
 | 
			
		||||
 *  \param[in] conn_src associated source connection.
 | 
			
		||||
 *  \param[in] conn_dst associated destination connection.
 | 
			
		||||
 *  \returns 0 on success, -1 on ERROR. */
 | 
			
		||||
static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
 | 
			
		||||
		   struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst)
 | 
			
		||||
{
 | 
			
		||||
	/*! When no destination connection is available (e.g. when only one
 | 
			
		||||
	 *  connection in loopback mode exists), then the source connection
 | 
			
		||||
	 *  shall be specified as destination connection */
 | 
			
		||||
 | 
			
		||||
	struct mgcp_rtp_end *rtp_end;
 | 
			
		||||
	struct mgcp_rtp_state *rtp_state;
 | 
			
		||||
	char ipbuf[INET6_ADDRSTRLEN];
 | 
			
		||||
	struct rtp_hdr *hdr = (struct rtp_hdr *)msgb_data(msg);
 | 
			
		||||
	int buflen = msgb_length(msg);
 | 
			
		||||
	char *dest_name;
 | 
			
		||||
	int len;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(conn_src);
 | 
			
		||||
	OSMO_ASSERT(conn_dst);
 | 
			
		||||
 | 
			
		||||
	LOGPENDP(endp, DRTP, LOGL_DEBUG, "delivering IuUP packet...\n");
 | 
			
		||||
 | 
			
		||||
	/* Note: In case of loopback configuration, both, the source and the
 | 
			
		||||
	 * destination will point to the same connection. */
 | 
			
		||||
	rtp_end = &conn_dst->end;
 | 
			
		||||
	rtp_state = &conn_src->state;
 | 
			
		||||
	dest_name = conn_dst->conn->name;
 | 
			
		||||
 | 
			
		||||
	/* Ensure we have an alternative SSRC in case we need it, see also
 | 
			
		||||
	 * gen_rtp_header() */
 | 
			
		||||
	if (rtp_state->alt_rtp_tx_ssrc == 0)
 | 
			
		||||
		rtp_state->alt_rtp_tx_ssrc = rand();
 | 
			
		||||
 | 
			
		||||
	if (!rtp_end->output_enabled) {
 | 
			
		||||
		rtpconn_rate_ctr_add(conn_dst, endp, RTP_DROPPED_PACKETS_CTR, 1);
 | 
			
		||||
		LOGPENDP(endp, DRTP, LOGL_DEBUG,
 | 
			
		||||
			 "output disabled, drop to %s %s "
 | 
			
		||||
			 "rtp_port:%u rtcp_port:%u\n",
 | 
			
		||||
			 dest_name,
 | 
			
		||||
			 osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
 | 
			
		||||
			 ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)
 | 
			
		||||
			);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Specs say, in IuUP, the RTP seqnum and timestamp should actually be
 | 
			
		||||
	 * ignored by the receiver, but still it's useful for debug purposes
 | 
			
		||||
	 * to set it. Moreover, it seems ip.access nano3g produces much worse
 | 
			
		||||
	 * audio output on the air side if timestamp is not set properly. */
 | 
			
		||||
	hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->codec->rate));
 | 
			
		||||
	hdr->sequence = osmo_htons(rtp_state->alt_rtp_tx_sequence);
 | 
			
		||||
	hdr->ssrc = rtp_state->alt_rtp_tx_ssrc;
 | 
			
		||||
 | 
			
		||||
	LOGPENDP(endp, DRTP, LOGL_DEBUG,
 | 
			
		||||
		 "process/send IuUP to %s %s rtp_port:%u rtcp_port:%u\n",
 | 
			
		||||
		 dest_name, osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
 | 
			
		||||
		 ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port));
 | 
			
		||||
 | 
			
		||||
	/* Forward a copy of the RTP data to a debug ip/port */
 | 
			
		||||
	forward_data_tap(rtp_end->rtp.fd, &conn_src->tap_out,
 | 
			
		||||
		     msg);
 | 
			
		||||
 | 
			
		||||
	len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port,
 | 
			
		||||
			    (char *)hdr, buflen);
 | 
			
		||||
 | 
			
		||||
	if (len <= 0)
 | 
			
		||||
		return len;
 | 
			
		||||
 | 
			
		||||
	rtpconn_rate_ctr_add(conn_dst, endp, RTP_PACKETS_TX_CTR, 1);
 | 
			
		||||
	rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
 | 
			
		||||
	rtp_state->alt_rtp_tx_sequence++;
 | 
			
		||||
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Received TNL primitive from IuUP layer FSM, transmit it further down to the
 | 
			
		||||
 * socket towards destination peer. */
 | 
			
		||||
static int _conn_iuup_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn_rtp *conn_rtp_dst = ctx;
 | 
			
		||||
	struct mgcp_conn *conn_dst = conn_rtp_dst->conn;
 | 
			
		||||
	struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph;
 | 
			
		||||
	struct mgcp_conn *conn_src;
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	struct rtp_hdr *rtph;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST));
 | 
			
		||||
 | 
			
		||||
	msg = oph->msg;
 | 
			
		||||
	talloc_steal(conn_rtp_dst->conn, msg);
 | 
			
		||||
 | 
			
		||||
	msgb_pull_to_l2(msg);
 | 
			
		||||
	rtph = (struct rtp_hdr *)msgb_push(msg, sizeof(*rtph));
 | 
			
		||||
	/* TODO: fill rtph properly: */
 | 
			
		||||
	*rtph = (struct rtp_hdr){
 | 
			
		||||
		.csrc_count = 0,
 | 
			
		||||
		.extension = 0,
 | 
			
		||||
		.padding = 0,
 | 
			
		||||
		.version = 2,
 | 
			
		||||
		.payload_type = conn_rtp_dst->end.codec->payload_type,
 | 
			
		||||
		.marker = 0,
 | 
			
		||||
		.sequence = 0,
 | 
			
		||||
		.timestamp = 0,
 | 
			
		||||
		.ssrc = 0
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	/* The destination of the destination conn is the source conn, right? */
 | 
			
		||||
	conn_src = _find_dst_conn(conn_dst);
 | 
			
		||||
	if (!conn_src) {
 | 
			
		||||
		LOG_CONN_RTP(conn_rtp_dst, LOGL_NOTICE,
 | 
			
		||||
			     "Couldn't find source conn for IuUP dst conn\n");
 | 
			
		||||
		/* If there's no sister connection we are either still
 | 
			
		||||
		 * initializing (so we want to send back Init (ACK)), or we are
 | 
			
		||||
		 * probably in loopback mode anyway, so use dst as src. */
 | 
			
		||||
		conn_src = conn_dst;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return mgcp_send_iuup(conn_dst->endp, msg, &conn_src->u.rtp, conn_rtp_dst);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Used to upgrade a regular RTP connection (MGCP_RTP_DEFAULT) to become a IuUP
 | 
			
		||||
 * connection (MGCP_RTP_IUUP) */
 | 
			
		||||
int mgcp_conn_iuup_init(struct mgcp_conn_rtp *conn_rtp)
 | 
			
		||||
{
 | 
			
		||||
	conn_rtp->type = MGCP_RTP_IUUP;
 | 
			
		||||
	conn_rtp->iuup.iui = osmo_iuup_instance_alloc(conn_rtp->conn, conn_rtp->conn->id);
 | 
			
		||||
	OSMO_ASSERT(conn_rtp->iuup.iui);
 | 
			
		||||
	osmo_iuup_instance_set_user_prim_cb(conn_rtp->iuup.iui, _conn_iuup_user_prim_cb, conn_rtp);
 | 
			
		||||
	osmo_iuup_instance_set_transport_prim_cb(conn_rtp->iuup.iui, _conn_iuup_transport_prim_cb, conn_rtp);
 | 
			
		||||
	conn_rtp->iuup.rfci_id_no_data = -1;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Cleanup specific IuUP connection (MGCP_RTP_IUUP) state, allocated by mgcp_conn_iuup_init() */
 | 
			
		||||
void mgcp_conn_iuup_cleanup(struct mgcp_conn_rtp *conn_rtp)
 | 
			
		||||
{
 | 
			
		||||
	osmo_iuup_instance_free(conn_rtp->iuup.iui);
 | 
			
		||||
	conn_rtp->iuup.iui = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Received RTP+IuUP pkt from socket of conn_rtp_src, build a TNL primitive to
 | 
			
		||||
 * push it further up the stack to the IuUP layer FSM to handle and/or bridge it */
 | 
			
		||||
int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
 | 
			
		||||
	struct mgcp_conn_rtp *conn_rtp_src = mc->conn_src;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	bool force_output_enabled = false;
 | 
			
		||||
	bool prev_output_enabled;
 | 
			
		||||
	struct osmo_sockaddr prev_rem_addr;
 | 
			
		||||
	uint16_t prev_rem_rtp_port;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(mgcp_conn_rtp_is_iuup(conn_rtp_src));
 | 
			
		||||
 | 
			
		||||
	if ((rc = check_rtp_iuup(conn_rtp_src, msg)) < 0)
 | 
			
		||||
		goto free_ret;
 | 
			
		||||
 | 
			
		||||
	if (!conn_rtp_src->iuup.configured) {
 | 
			
		||||
		/* We received the first message without sending any, the peer is the active side (RNC). */
 | 
			
		||||
		rc = _conn_iuup_configure_as_passive(conn_rtp_src);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			goto free_ret;
 | 
			
		||||
		/* We need to force allowance of RTP containing Init-ACK back: */
 | 
			
		||||
		prev_output_enabled = conn_rtp_src->end.output_enabled;
 | 
			
		||||
		conn_rtp_src->end.output_enabled = true;
 | 
			
		||||
		force_output_enabled = true;
 | 
			
		||||
		/* Fill in the peer address so that we can send Init-ACK back: */
 | 
			
		||||
		prev_rem_addr = conn_rtp_src->end.addr;
 | 
			
		||||
		prev_rem_rtp_port = conn_rtp_src->end.rtp_port;
 | 
			
		||||
		conn_rtp_src->end.addr = *mc->from_addr;
 | 
			
		||||
		conn_rtp_src->end.rtp_port = htons(osmo_sockaddr_port(&mc->from_addr->u.sa));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = _conn_iuup_rtp_pl_up(conn_rtp_src, msg);
 | 
			
		||||
 | 
			
		||||
	if (force_output_enabled) {
 | 
			
		||||
		conn_rtp_src->end.output_enabled = prev_output_enabled;
 | 
			
		||||
		conn_rtp_src->end.addr = prev_rem_addr;
 | 
			
		||||
		conn_rtp_src->end.rtp_port = prev_rem_rtp_port;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
free_ret:
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Build IuUP RNL Data primitive from msg containing an incoming RTP pkt from
 | 
			
		||||
 * peer and send it down the IuUP layer towards the destination as IuUP/RTP: */
 | 
			
		||||
int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn_rtp *conn_dest_rtp, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_iuup_rnl_prim *irp;
 | 
			
		||||
	struct rtp_hdr *rtph;
 | 
			
		||||
	int rc = -1;
 | 
			
		||||
	int iuup_length = 0;
 | 
			
		||||
	int8_t rfci;
 | 
			
		||||
 | 
			
		||||
	/* Tx RNL-DATA.req */
 | 
			
		||||
	rtph = (struct rtp_hdr *)msgb_data(msg);
 | 
			
		||||
	msgb_pull(msg, sizeof(*rtph));
 | 
			
		||||
 | 
			
		||||
	/* FIXME: validate amr packets */
 | 
			
		||||
	irp = osmo_iuup_rnl_prim_alloc(conn_dest_rtp->conn, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE);
 | 
			
		||||
	irp->u.data.frame_nr = htons(rtph->sequence) % 16;
 | 
			
		||||
 | 
			
		||||
	/* TODO: CMR handling & multiple frames handling */
 | 
			
		||||
 | 
			
		||||
	if (strcmp(conn_src_rtp->end.codec->subtype_name, "AMR") != 0) {
 | 
			
		||||
		LOG_CONN_RTP(conn_src_rtp, LOGL_ERROR,
 | 
			
		||||
			     "Bridge RTP=>IuUP: Bridging src codec %s to IuUP AMR not supported\n",
 | 
			
		||||
			     conn_src_rtp->end.codec->subtype_name);
 | 
			
		||||
		goto free_ret;
 | 
			
		||||
	}
 | 
			
		||||
	if (mgcp_codec_amr_is_octet_aligned(conn_src_rtp->end.codec)) {
 | 
			
		||||
		struct amr_hdr *amr_hdr = (struct amr_hdr *) msgb_data(msg);
 | 
			
		||||
		if (msgb_length(msg) < (sizeof(*amr_hdr))) {
 | 
			
		||||
			LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE,
 | 
			
		||||
				     "Bridge RTP=>IuUP: too short for AMR OA hdr (%u)\n", msgb_length(msg));
 | 
			
		||||
			goto free_ret;
 | 
			
		||||
		}
 | 
			
		||||
		if (amr_hdr->ft >= AMR_FT_MAX) {
 | 
			
		||||
			LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR OA ft=%u\n", amr_hdr->ft);
 | 
			
		||||
			goto free_ret;
 | 
			
		||||
		}
 | 
			
		||||
		if ((rfci =  _conn_iuup_amr_ft_2_rfci(conn_dest_rtp, amr_hdr->ft)) < 0) {
 | 
			
		||||
			LOG_CONN_RTP(conn_dest_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: No RFCI found for AMR OA ft=%u\n", amr_hdr->ft);
 | 
			
		||||
			goto free_ret;
 | 
			
		||||
		}
 | 
			
		||||
		irp->u.data.fqc = amr_hdr->q;
 | 
			
		||||
		irp->u.data.rfci = rfci;
 | 
			
		||||
		msgb_pull(msg, 2);
 | 
			
		||||
	} else {
 | 
			
		||||
		uint8_t *amr_bwe_hdr = (uint8_t *) msgb_data(msg);
 | 
			
		||||
		int8_t ft;
 | 
			
		||||
		if (msgb_length(msg) < 2) {
 | 
			
		||||
			LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE,
 | 
			
		||||
				     "Bridge RTP=>IuUP: too short for AMR BE hdr (%u)\n", msgb_length(msg));
 | 
			
		||||
			goto free_ret;
 | 
			
		||||
		}
 | 
			
		||||
		ft = ((amr_bwe_hdr[0] & 0x07) << 1) | ((amr_bwe_hdr[1] & 0x80) >> 7);
 | 
			
		||||
		if (ft >= AMR_FT_MAX) {
 | 
			
		||||
			LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR BE ft=%u\n", ft);
 | 
			
		||||
			goto free_ret;
 | 
			
		||||
		}
 | 
			
		||||
		if ((rfci =  _conn_iuup_amr_ft_2_rfci(conn_dest_rtp, ft)) < 0) {
 | 
			
		||||
			LOG_CONN_RTP(conn_dest_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: No RFCI found for AMR BE ft=%u\n", ft);
 | 
			
		||||
			goto free_ret;
 | 
			
		||||
		}
 | 
			
		||||
		irp->u.data.fqc = ((amr_bwe_hdr[1] & 0x40) >> 6);
 | 
			
		||||
		irp->u.data.rfci = rfci;
 | 
			
		||||
		rc = iuup_length = osmo_amr_bwe_to_iuup(msgb_data(msg), msgb_length(msg));
 | 
			
		||||
		if (rc < 0) {
 | 
			
		||||
			LOG_CONN_RTP(conn_dest_rtp, LOGL_ERROR, "Bridge RTP=>IuUP: Failed convert the RTP/AMR to IuUP payload\n");
 | 
			
		||||
			return rc;
 | 
			
		||||
		}
 | 
			
		||||
		msgb_trim(msg, iuup_length);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	irp->oph.msg->l3h = msgb_put(irp->oph.msg, msgb_length(msg));
 | 
			
		||||
	memcpy(irp->oph.msg->l3h, msgb_data(msg), msgb_length(msg));
 | 
			
		||||
	if ((rc = osmo_iuup_rnl_prim_down(conn_dest_rtp->iuup.iui, irp)) != 0)
 | 
			
		||||
		LOG_CONN_RTP(conn_dest_rtp, LOGL_ERROR, "Bridge RTP=>IuUP: Failed Tx RTP payload down the IuUP layer\n");
 | 
			
		||||
	return rc;
 | 
			
		||||
 | 
			
		||||
free_ret:
 | 
			
		||||
	msgb_free(irp->oph.msg);
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Build IuUP RNL Data primitive from msg containing dummy content and send it
 | 
			
		||||
 * down the IuUP layer towards the destination as IuUP/RTP: */
 | 
			
		||||
int mgcp_conn_iuup_send_dummy(struct mgcp_conn_rtp *conn_rtp)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_iuup_rnl_prim *irp;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	if (conn_rtp->iuup.rfci_id_no_data == -1) {
 | 
			
		||||
		LOG_CONN_RTP(conn_rtp, LOGL_NOTICE, "No RFCI NO_DATA found, unable to send dummy packet\n");
 | 
			
		||||
		return -ENOTSUP;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	irp = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE);
 | 
			
		||||
	irp->u.data.frame_nr = 0;
 | 
			
		||||
	irp->u.data.fqc = IUUP_FQC_FRAME_GOOD;
 | 
			
		||||
	irp->u.data.rfci = conn_rtp->iuup.rfci_id_no_data;
 | 
			
		||||
	irp->oph.msg->l3h = irp->oph.msg->tail;
 | 
			
		||||
	if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp)) != 0) {
 | 
			
		||||
		LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed Tx RTP dummy payload down the IuUP layer\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -110,8 +110,7 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
 | 
			
		||||
 | 
			
		||||
	/* Special handling for RTP connections */
 | 
			
		||||
	if (conn->type == MGCP_CONN_TYPE_RTP) {
 | 
			
		||||
		conn->u.rtp.end.output_enabled =
 | 
			
		||||
		    conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0;
 | 
			
		||||
		conn->u.rtp.end.output_enabled = !!(conn->mode & MGCP_CONN_SEND_ONLY);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn));
 | 
			
		||||
@@ -121,7 +120,7 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
 | 
			
		||||
 | 
			
		||||
	/* Special handling für RTP connections */
 | 
			
		||||
	if (conn->type == MGCP_CONN_TYPE_RTP) {
 | 
			
		||||
		LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %d\n",
 | 
			
		||||
		LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %u\n",
 | 
			
		||||
			 conn->u.rtp.end.output_enabled);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -48,7 +48,7 @@
 | 
			
		||||
#include <osmocom/mgcp/debug.h>
 | 
			
		||||
#include <osmocom/codec/codec.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_e1.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp_iuup.h>
 | 
			
		||||
 | 
			
		||||
#define RTP_SEQ_MOD		(1 << 16)
 | 
			
		||||
#define RTP_MAX_DROPOUT		3000
 | 
			
		||||
@@ -59,7 +59,7 @@ enum rtp_proto {
 | 
			
		||||
	MGCP_PROTO_RTCP,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
 | 
			
		||||
void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
 | 
			
		||||
				 int id, int inc)
 | 
			
		||||
{
 | 
			
		||||
	struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
 | 
			
		||||
@@ -156,7 +156,7 @@ void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
 | 
			
		||||
/* This does not need to be a precision timestamp and
 | 
			
		||||
 * is allowed to wrap quite fast. The returned value is
 | 
			
		||||
 * 1/codec_rate seconds. */
 | 
			
		||||
static uint32_t get_current_ts(unsigned codec_rate)
 | 
			
		||||
uint32_t mgcp_get_current_ts(unsigned codec_rate)
 | 
			
		||||
{
 | 
			
		||||
	struct timespec tp;
 | 
			
		||||
	uint64_t ret;
 | 
			
		||||
@@ -529,7 +529,7 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
 | 
			
		||||
	rtp_hdr = (struct rtp_hdr *)msgb_data(msg);
 | 
			
		||||
	seq = ntohs(rtp_hdr->sequence);
 | 
			
		||||
	timestamp = ntohl(rtp_hdr->timestamp);
 | 
			
		||||
	arrival_time = get_current_ts(rtp_end->codec->rate);
 | 
			
		||||
	arrival_time = mgcp_get_current_ts(rtp_end->codec->rate);
 | 
			
		||||
	ssrc = ntohl(rtp_hdr->ssrc);
 | 
			
		||||
	marker_bit = !!rtp_hdr->marker;
 | 
			
		||||
	transit = arrival_time - timestamp;
 | 
			
		||||
@@ -789,7 +789,7 @@ static int amr_oa_check(char *data, int len)
 | 
			
		||||
 | 
			
		||||
/* Forward data to a debug tap. This is debug function that is intended for
 | 
			
		||||
 * debugging the voice traffic with tools like gstreamer */
 | 
			
		||||
static void forward_data(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg)
 | 
			
		||||
void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
@@ -815,7 +815,7 @@ static void gen_rtp_header(struct msgb *msg, struct mgcp_rtp_end *rtp_end,
 | 
			
		||||
 | 
			
		||||
	hdr->version = 2;
 | 
			
		||||
	hdr->payload_type = rtp_end->codec->payload_type;
 | 
			
		||||
	hdr->timestamp = osmo_htonl(get_current_ts(rtp_end->codec->rate));
 | 
			
		||||
	hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->codec->rate));
 | 
			
		||||
	hdr->sequence = osmo_htons(state->alt_rtp_tx_sequence);
 | 
			
		||||
	hdr->ssrc = state->alt_rtp_tx_ssrc;
 | 
			
		||||
}
 | 
			
		||||
@@ -975,7 +975,7 @@ static int check_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
 | 
			
		||||
	 * the length is because we currently handle IUUP packets as RTP
 | 
			
		||||
	 * packets, so they must pass this check, if we weould be more
 | 
			
		||||
	 * strict here, we would possibly break 3G. (see also FIXME note
 | 
			
		||||
	 * below */
 | 
			
		||||
	 * below.*/
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -1013,6 +1013,19 @@ static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
 | 
			
		||||
			 "endpoint type is MGCP_OSMUX_BSC_NAT, "
 | 
			
		||||
			 "using osmux_xfrm_to_osmux() to forward data through OSMUX\n");
 | 
			
		||||
		return osmux_xfrm_to_osmux((char*)msgb_data(msg), msgb_length(msg), conn_dst);
 | 
			
		||||
	case MGCP_RTP_IUUP:
 | 
			
		||||
		if (proto == MGCP_PROTO_RTP) {
 | 
			
		||||
			LOGPENDP(endp, DRTP, LOGL_DEBUG,
 | 
			
		||||
				 "endpoint type is MGCP_RTP_IUUP, "
 | 
			
		||||
				 "using mgcp_conn_iuup_send_rtp() to forward data over IuUP\n");
 | 
			
		||||
			return mgcp_conn_iuup_send_rtp(conn_src, conn_dst, msg);
 | 
			
		||||
		}
 | 
			
		||||
		/* RTCP: we forward as usual for regular RTP connection */
 | 
			
		||||
		LOGPENDP(endp, DRTP, LOGL_DEBUG,
 | 
			
		||||
			 "endpoint type is MGCP_RTP_IUUP and proto!=MGCP_PROTO_RTP, "
 | 
			
		||||
			 "using mgcp_send() to forward data directly\n");
 | 
			
		||||
		return mgcp_send(endp, false,
 | 
			
		||||
				 mc->from_addr, msg, conn_src, conn_dst);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* If the data has not been handled/forwarded until here, it will
 | 
			
		||||
@@ -1073,8 +1086,11 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
 | 
			
		||||
	if (check_rtp_destin(conn) != 0)
 | 
			
		||||
		goto failed;
 | 
			
		||||
 | 
			
		||||
	rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr,
 | 
			
		||||
			   conn->end.rtp_port, rtp_dummy_payload, sizeof(rtp_dummy_payload));
 | 
			
		||||
	if (mgcp_conn_rtp_is_iuup(conn))
 | 
			
		||||
		rc = mgcp_conn_iuup_send_dummy(conn);
 | 
			
		||||
	else
 | 
			
		||||
		rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr, conn->end.rtp_port,
 | 
			
		||||
				   rtp_dummy_payload, sizeof(rtp_dummy_payload));
 | 
			
		||||
 | 
			
		||||
	if (rc == -1)
 | 
			
		||||
		goto failed;
 | 
			
		||||
@@ -1138,7 +1154,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
 | 
			
		||||
	 * course unable to patch the payload type. A situation like this
 | 
			
		||||
	 * should not occur if transcoding is consequently avoided. Until
 | 
			
		||||
	 * we have transcoding support in osmo-mgw we can not resolve this. */
 | 
			
		||||
	if (is_rtp) {
 | 
			
		||||
	if (is_rtp && conn_dst->type != MGCP_RTP_IUUP) {
 | 
			
		||||
		rc = mgcp_patch_pt(conn_src, conn_dst, msg);
 | 
			
		||||
		if (rc < 0) {
 | 
			
		||||
			LOGPENDP(endp, DRTP, LOGL_DEBUG,
 | 
			
		||||
@@ -1185,7 +1201,9 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
 | 
			
		||||
				mgcp_patch_and_count(endp, rtp_state, rtp_end,
 | 
			
		||||
						     addr, msg);
 | 
			
		||||
 | 
			
		||||
			if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) {
 | 
			
		||||
			if (mgcp_conn_rtp_is_iuup(conn_dst) || mgcp_conn_rtp_is_iuup(conn_src)) {
 | 
			
		||||
				/* the iuup code will correctly transform to the correct AMR mode */
 | 
			
		||||
			} else if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) {
 | 
			
		||||
				rc = amr_oa_bwe_convert(endp, msg,
 | 
			
		||||
							conn_dst->end.codec->param.amr_octet_aligned);
 | 
			
		||||
				if (rc < 0) {
 | 
			
		||||
@@ -1211,27 +1229,9 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
 | 
			
		||||
				);
 | 
			
		||||
 | 
			
		||||
			/* Forward a copy of the RTP data to a debug ip/port */
 | 
			
		||||
			forward_data(rtp_end->rtp.fd, &conn_src->tap_out,
 | 
			
		||||
			forward_data_tap(rtp_end->rtp.fd, &conn_src->tap_out,
 | 
			
		||||
				     msg);
 | 
			
		||||
 | 
			
		||||
			/* FIXME: HACK HACK HACK. See OS#2459.
 | 
			
		||||
			 * The ip.access nano3G needs the first RTP payload's first two bytes to read hex
 | 
			
		||||
			 * 'e400', or it will reject the RAB assignment. It seems to not harm other femto
 | 
			
		||||
			 * cells (as long as we patch only the first RTP payload in each stream).
 | 
			
		||||
			 */
 | 
			
		||||
			if (!rtp_state->patched_first_rtp_payload
 | 
			
		||||
			    && conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
 | 
			
		||||
				uint8_t *data = msgb_data(msg) + 12;
 | 
			
		||||
				if (data[0] == 0xe0) {
 | 
			
		||||
					data[0] = 0xe4;
 | 
			
		||||
					data[1] = 0x00;
 | 
			
		||||
					rtp_state->patched_first_rtp_payload = true;
 | 
			
		||||
					LOGPENDP(endp, DRTP, LOGL_DEBUG,
 | 
			
		||||
						 "Patching over first two bytes"
 | 
			
		||||
						 " to fake an IuUP Initialization Ack\n");
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port,
 | 
			
		||||
					    (char *)msgb_data(msg), msgb_length(msg));
 | 
			
		||||
 | 
			
		||||
@@ -1291,6 +1291,9 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
 | 
			
		||||
	 *  destination connection is known the RTP packet is sent via
 | 
			
		||||
	 *  the destination connection. */
 | 
			
		||||
 | 
			
		||||
	/* If source is IuUP, we need to handle state, forward it through specific bridge path: */
 | 
			
		||||
	if (mgcp_conn_rtp_is_iuup(conn_src) && mc->proto == MGCP_PROTO_RTP)
 | 
			
		||||
		return mgcp_conn_iuup_dispatch_rtp(msg);
 | 
			
		||||
 | 
			
		||||
	 /* Check if the connection is in loopback mode, if yes, just send the
 | 
			
		||||
	 * incoming data back to the origin */
 | 
			
		||||
@@ -1468,7 +1471,7 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
 | 
			
		||||
	msgb_put(msg, ret);
 | 
			
		||||
 | 
			
		||||
	LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s:%u\n",
 | 
			
		||||
		     proto == MGCP_PROTO_RTP ? "RTP" : "RTPC",
 | 
			
		||||
		     proto == MGCP_PROTO_RTP ? "RTP" : "RTCP",
 | 
			
		||||
		     msgb_length(msg), osmo_sockaddr_ntop(&addr.u.sa, ipbuf),
 | 
			
		||||
		     osmo_sockaddr_port(&addr.u.sa));
 | 
			
		||||
 | 
			
		||||
@@ -1506,7 +1509,7 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
 | 
			
		||||
	/* FIXME: count RTP and RTCP separately, also count IuUP payload-less separately */
 | 
			
		||||
 | 
			
		||||
	/* Forward a copy of the RTP data to a debug ip/port */
 | 
			
		||||
	forward_data(fd->fd, &conn_src->tap_in, msg);
 | 
			
		||||
	forward_data_tap(fd->fd, &conn_src->tap_in, msg);
 | 
			
		||||
 | 
			
		||||
	rc = rx_rtp(msg);
 | 
			
		||||
 | 
			
		||||
@@ -1515,6 +1518,7 @@ out:
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Note: This function is able to handle RTP and RTCP */
 | 
			
		||||
static int rx_rtp(struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
 | 
			
		||||
@@ -1531,7 +1535,8 @@ static int rx_rtp(struct msgb *msg)
 | 
			
		||||
	 * framing mode (octet-aligned vs. bandwith-efficient is explicitly
 | 
			
		||||
	 * define, then we check if the incoming payload matches that
 | 
			
		||||
	 * expectation. */
 | 
			
		||||
	if (amr_oa_bwe_convert_indicated(conn_src->end.codec)) {
 | 
			
		||||
	if (mc->proto == MGCP_PROTO_RTP &&
 | 
			
		||||
	    amr_oa_bwe_convert_indicated(conn_src->end.codec)) {
 | 
			
		||||
		int oa = amr_oa_check((char*)msgb_data(msg), msgb_length(msg));
 | 
			
		||||
		if (oa < 0)
 | 
			
		||||
			return -1;
 | 
			
		||||
 
 | 
			
		||||
@@ -46,6 +46,7 @@
 | 
			
		||||
#include <osmocom/mgcp/mgcp_sdp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_codec.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_iuup.h>
 | 
			
		||||
 | 
			
		||||
/* Contains the last successfully resolved endpoint name. This variable is used
 | 
			
		||||
 * for the unit-tests to verify that the endpoint was correctly resolved. */
 | 
			
		||||
@@ -147,7 +148,13 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp,
 | 
			
		||||
	struct mgcp_conn_rtp *conn_dst = conn;
 | 
			
		||||
	struct mgcp_conn *_conn;
 | 
			
		||||
 | 
			
		||||
	if (conn->type != MGCP_RTP_DEFAULT && !mgcp_conn_rtp_is_osmux(conn)) {
 | 
			
		||||
	switch (conn->type) {
 | 
			
		||||
	case MGCP_RTP_DEFAULT:
 | 
			
		||||
	case MGCP_OSMUX_BSC:
 | 
			
		||||
	case MGCP_OSMUX_BSC_NAT:
 | 
			
		||||
	case MGCP_RTP_IUUP:
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
 | 
			
		||||
			 "RTP-setup: Endpoint is not configured as RTP default, stopping here!\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
@@ -1028,6 +1035,11 @@ mgcp_header_done:
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION));
 | 
			
		||||
		goto error2;
 | 
			
		||||
	}
 | 
			
		||||
	/* Upgrade the conn type RTP_DEFAULT->RTP_IUUP if needed based on requested codec: */
 | 
			
		||||
	/* TODO: "codec" probably needs to be moved from endp to conn */
 | 
			
		||||
	if (conn->type == MGCP_RTP_DEFAULT && strcmp(conn->end.codec->subtype_name, "VND.3GPP.IUFP") == 0) {
 | 
			
		||||
		rc = mgcp_conn_iuup_init(conn);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
 | 
			
		||||
					     trunk->audio_fmtp_extra);
 | 
			
		||||
 
 | 
			
		||||
@@ -98,7 +98,7 @@ static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
 | 
			
		||||
/* Helper function to update codec map information with additional data from
 | 
			
		||||
 * SDP, called from: mgcp_parse_sdp_data() */
 | 
			
		||||
static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
 | 
			
		||||
			  int payload, const char *audio_name)
 | 
			
		||||
			  int payload_type, const char *audio_name)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
@@ -110,7 +110,7 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
 | 
			
		||||
		/* Note: We can only update payload codecs that already exist
 | 
			
		||||
		 * in our codec list. If we get an unexpected payload type,
 | 
			
		||||
		 * we just drop it */
 | 
			
		||||
		if (codecs[i].payload_type != payload)
 | 
			
		||||
		if (codecs[i].payload_type != payload_type)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (sscanf(audio_name, "%63[^/]/%d/%d",
 | 
			
		||||
@@ -127,7 +127,7 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload,
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload_type,
 | 
			
		||||
	     audio_name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -334,7 +334,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
 | 
			
		||||
	void *tmp_ctx = talloc_new(NULL);
 | 
			
		||||
	struct mgcp_rtp_end *rtp;
 | 
			
		||||
 | 
			
		||||
	int payload;
 | 
			
		||||
	int payload_type;
 | 
			
		||||
	int ptime, ptime2 = 0;
 | 
			
		||||
	char audio_name[64];
 | 
			
		||||
	int port, rc;
 | 
			
		||||
@@ -355,8 +355,8 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
 | 
			
		||||
			/* skip these SDP attributes */
 | 
			
		||||
			break;
 | 
			
		||||
		case 'a':
 | 
			
		||||
			if (sscanf(line, "a=rtpmap:%d %63s", &payload, audio_name) == 2) {
 | 
			
		||||
				codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name);
 | 
			
		||||
			if (sscanf(line, "a=rtpmap:%d %63s", &payload_type, audio_name) == 2) {
 | 
			
		||||
				codecs_update(tmp_ctx, codecs, codecs_used, payload_type, audio_name);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ EXTRA_DIST = \
 | 
			
		||||
	mgcp_test.ok \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
noinst_PROGRAMS = \
 | 
			
		||||
check_PROGRAMS = \
 | 
			
		||||
	mgcp_test \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -868,7 +868,7 @@ static void test_messages(void)
 | 
			
		||||
					printf("Connection mode not set\n");
 | 
			
		||||
 | 
			
		||||
				OSMO_ASSERT(conn->end.output_enabled
 | 
			
		||||
					    == (conn->conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0));
 | 
			
		||||
					    == !!(conn->conn->mode & MGCP_CONN_SEND_ONLY));
 | 
			
		||||
 | 
			
		||||
				conn->conn->mode |= CONN_UNMODIFIED;
 | 
			
		||||
 | 
			
		||||
@@ -1896,16 +1896,13 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
 | 
			
		||||
		.codecs = {
 | 
			
		||||
			{
 | 
			
		||||
				{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
 | 
			
		||||
				{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				{ 122, "AMR/8000", &amr_param_octet_aligned_false, },
 | 
			
		||||
				{ 121, "AMR/8000", &amr_param_octet_aligned_true, },
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		.expect = {
 | 
			
		||||
			{ .payload_type_map = {111, 121}, },
 | 
			
		||||
			{ .payload_type_map = {112, 122} },
 | 
			
		||||
			{ .payload_type_map = {111, 122}, },
 | 
			
		||||
			{ .end = true },
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
@@ -1914,15 +1911,13 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
 | 
			
		||||
		.codecs = {
 | 
			
		||||
			{
 | 
			
		||||
				{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
 | 
			
		||||
				{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				{ 122, "AMR/8000", &amr_param_octet_aligned_unset, },
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		.expect = {
 | 
			
		||||
			{ .payload_type_map = {111, -EINVAL}, },
 | 
			
		||||
			{ .payload_type_map = {112, 122} },
 | 
			
		||||
			{ .payload_type_map = {111, 122}, },
 | 
			
		||||
			{ .end = true },
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
@@ -1931,15 +1926,13 @@ static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translat
 | 
			
		||||
		.codecs = {
 | 
			
		||||
			{
 | 
			
		||||
				{ 111, "AMR/8000", &amr_param_octet_aligned_true, },
 | 
			
		||||
				{ 112, "AMR/8000", &amr_param_octet_aligned_false, },
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				{ 122, "AMR/8000", NULL, },
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		.expect = {
 | 
			
		||||
			{ .payload_type_map = {111, -EINVAL}, },
 | 
			
		||||
			{ .payload_type_map = {112, 122} },
 | 
			
		||||
			{ .payload_type_map = {111, 122}, },
 | 
			
		||||
			{ .end = true },
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 
 | 
			
		||||
@@ -1347,32 +1347,24 @@ Testing mgcp_codec_pt_translate()
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 0) -> -22
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
 | 
			
		||||
#4: conn1 has no codecs
 | 
			
		||||
#4: conn1 has no codecs
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    1:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    2: 111 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    (none)
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
 | 
			
		||||
    0: 112 AMR/8000/1 octet-aligned=1  -> rc=0
 | 
			
		||||
    1:   0 PCMU/8000/1  -> rc=0
 | 
			
		||||
    2: 111 GSM-HR-08/8000/1  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    (none)
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 0) -> -22
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
    0: 122 AMR/8000 octet-aligned=0  -> rc=0
 | 
			
		||||
    1: 121 AMR/8000 octet-aligned=1  -> rc=0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
 | 
			
		||||
#5: test AMR with differing octet-aligned settings
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 111 AMR/8000 octet-aligned=1  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
 | 
			
		||||
    0: 122 AMR/8000 octet-aligned=0  -> rc=0
 | 
			
		||||
 - mgcp_codec_pt_translate(conn0, conn1, 111) -> 122
 | 
			
		||||
    0: 111 AMR/8000 octet-aligned=1  -> rc=0
 | 
			
		||||
    1: 112 AMR/8000 octet-aligned=0  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
 - mgcp_codec_pt_translate(conn1, conn0, 122) -> 111
 | 
			
		||||
#6: test AMR with missing octet-aligned settings (defaults to 0)
 | 
			
		||||
 - add codecs on conn0:
 | 
			
		||||
    0: 111 AMR/8000 octet-aligned=1  -> rc=0
 | 
			
		||||
 - add codecs on conn1:
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ EXTRA_DIST = \
 | 
			
		||||
	mgcp_client_test.err \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
noinst_PROGRAMS = \
 | 
			
		||||
check_PROGRAMS = \
 | 
			
		||||
	mgcp_client_test \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user