Compare commits

...

4 Commits

Author SHA1 Message Date
Alexander Couzens
e6d7bb7388 WIP: add IuuP support
Change-Id: I88cfc4fcbd6efa02a6d97a0c602caf9344c6c04d
2022-01-02 23:36:31 +01:00
Pau Espin Pedrol
8a34e1a8f1 WIP: Initial IuUP support using proper FSMs
Change-Id: I6694a21480b25ab8f35d375295be6601ce38e31d
2021-12-30 21:23:03 +01:00
Philipp Maier
3a912b2db2 mgcp_client: add new codec IUUP as vnd.3gpp.iufp
3GPP TS 25.414 5.1.3.3.1.6 specifies that IuUP can use RTP as transport.
The payload type is specified from 97-127, which shall be ignored on the
receiving end anyway.

The payload type number we use shall be 97 by default.

Change-Id: Ifd1210a897743396899f34457c96e6fd2109c6b3
Related: OS#5152
2021-12-30 21:23:03 +01:00
Pau Espin Pedrol
cac10c94c3 Define mgcp_rtp_end.output_enabled as bool
Change-Id: I55f7796ef774f86050041f2c5e3a2f8f7d1f56df
2021-12-24 15:13:34 +01:00
15 changed files with 553 additions and 71 deletions

View File

@@ -13,4 +13,5 @@ noinst_HEADERS = \
mgcp_e1.h \
mgcp_network.h \
mgcp_protocol.h \
mgcp_iuup.h \
$(NULL)

View File

@@ -8,7 +8,11 @@
#define PTYPE_UNDEFINED (-1)
#include <stdbool.h>
struct mgcp_conn_rtp;
struct mgcp_codec_param;
struct mgcp_rtp_codec;
void mgcp_codec_summary(struct mgcp_conn_rtp *conn);
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn);
@@ -17,3 +21,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);

View File

@@ -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,13 @@ struct mgcp_conn_rtp {
} stats;
} osmux;
struct {
struct osmo_iuup_instance *iui;
bool active_init; /* true: Send IuUP Init */
bool first_rtp_pkt_received;
uint8_t frame_num; /* shouldn't this part of the IUUP layer? */
} iuup;
struct rate_ctr_group *rate_ctr_group;
};
@@ -176,6 +185,11 @@ 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);

View File

@@ -0,0 +1,32 @@
/* Message connection list handling */
/*
* (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);

View File

@@ -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;

View File

@@ -49,6 +49,7 @@ enum mgcp_codecs {
CODEC_GSMHR_8000_1 = 111,
CODEC_AMR_8000_1 = 112,
CODEC_AMRWB_16000_1 = 113,
CODEC_IUUP = 97,
};
/* Note: when new codec types are added, the corresponding value strings
* in mgcp_client.c (codec_table) must be updated as well. Enumerations

View File

@@ -38,6 +38,7 @@
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <stdlib.h>
#include <limits.h>
@@ -59,6 +60,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_IUUP, "VND.3GPP.IuFP/16000/1" },
{ 0, NULL },
};
@@ -97,7 +99,7 @@ enum mgcp_codecs map_str_to_codec(const char *str)
codec_name = extract_codec_name(osmo_mgcpc_codec_names[i].str);
if (!codec_name)
continue;
if (strcmp(codec_name, str_buf) == 0)
if (strcasecmp(codec_name, str_buf) == 0)
return osmo_mgcpc_codec_names[i].value;
}

View File

@@ -48,4 +48,5 @@ libosmo_mgcp_a_SOURCES = \
mgcp_ctrl.c \
mgcp_ratectr.c \
mgcp_e1.c \
mgcp_iuup.c \
$(NULL)

View File

@@ -25,6 +25,7 @@
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <errno.h>
#include <strings.h>
/* Helper function to dump codec information of a specified codec to a printable
* string, used by dump_codec_summary() */
@@ -241,6 +242,8 @@ int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *aud
codec->payload_type = 112;
else if (!strcmp(codec->subtype_name, "AMR-WB"))
codec->payload_type = 113;
else if (!strcasecmp(codec->subtype_name, "VND.3GPP.IuFP"))
codec->payload_type = 97;
}
/* If we could not determine a payload type we assume that
@@ -355,7 +358,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;
@@ -379,7 +382,7 @@ static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *c
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))
if (mgcp_codec_amr_is_octet_aligned(codec_a) != mgcp_codec_amr_is_octet_aligned(codec_b))
return false;
}
@@ -417,7 +420,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);

View File

@@ -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);

View File

@@ -0,0 +1,447 @@
/*
* (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/gsm/iuup.h>
#include <osmocom/netif/amr.h>
#include <osmocom/netif/rtp.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_iuup.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/debug.h>
#define MGW_IUUP_MSGB_SIZE 4096
static struct osmo_iuup_rnl_config def_configure_req = {
.transparent = false,
.active = true,
.supported_versions_mask = 0x0003,
.num_rfci = 0,
.num_subflows = 0,
/* .delivery_err_sdu = All set to 0 (YES) by default, */
.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 },
};
static struct mgcp_conn *_find_dst_conn(struct mgcp_conn *conn)
{
/* Find a destination connection. */
/* 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;
}
static int _conn_iuup_configure_as_passive(struct mgcp_conn_rtp *conn_rtp, struct msgb *msg)
{
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)
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed configuring IuUP layer\n");
return rc;
}
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;
}
static int _conn_iuup_rx_rnl_data(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp)
{
struct mgcp_conn *conn_src, *conn_dst;
struct mgcp_conn_rtp *conn_src_rtp, *conn_rtp_dst;
int rc;
uint8_t rfci, frame_nr, fqc;
int ft;
struct msgb *msg;
struct amr_hdr *amr_hdr;
uint8_t *amr_data;
ssize_t amr_length = 0;
struct rtp_hdr *rtp_hdr;
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:
/* We simply forward the msg, without freeing it: */
talloc_steal(conn_dst, 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;
break;
case MGCP_RTP_DEFAULT:
/* FIXME: We probably need transcoding here?! Or at least look up AMR modes and translate to related RFCI */
rfci = irp->u.data.rfci;
frame_nr = irp->u.data.frame_nr;
fqc = irp->u.data.fqc;
msg = irp->oph.msg;
ft = osmo_amr_bytes_to_ft(msgb_l3len(msg));
if (ft < 0) {
/* FIXME LOGP */
return ft;
}
msgb_pull_to_l3(msg);
LOGP(DLMGCP, LOGL_ERROR, "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 {
if (msgb_tailroom(msg) < 2) {
/* FIXME not enought tailroom */
return -4;
}
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);
LOGP(DLMGCP, LOGL_ERROR, "Convert Iuup -> AMR bwe: amr[0] 0x%x 0x%x\n", amr_data[0], amr_data[1]);
amr_data[1] |= ((ft & 0x1) << 7) | (((!fqc) & 0x1) << 6);
LOGP(DLMGCP, LOGL_ERROR, "Convert Iuup -> AMR bwe: amr[0] 0x%x 0x%x\n", amr_data[0], amr_data[1]);
amr_length = (osmo_amr_bits(ft) + 10 + 7) / 8;
msgb_trim(msg, amr_length);
}
rtp_hdr = (struct rtp_hdr *) msgb_push(msg, sizeof(struct rtp_hdr));
*rtp_hdr = (struct rtp_hdr){
.csrc_count = 0,
.extension = 0,
.padding = 0,
.version = 0,
.payload_type = 0,
.marker = 0,
.sequence = 0,
.timestamp = 0,
.ssrc = 0
};
conn_src = _find_dst_conn(conn_dst);
if (conn_src)
conn_src_rtp = &conn_src->u.rtp;
else
conn_src_rtp = &conn_dst->u.rtp;
rc = mgcp_send(conn_dst->endp, 1,
NULL, msg, conn_src_rtp, conn_rtp_dst);
break;
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);
}
free_ret:
msgb_free(irp->oph.msg);
return rc;
}
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;
conn_dst = _find_dst_conn(conn_rtp_src->conn);
if (!conn_dst)
return 0; /* FIXME: that probably means we potentially need to Init the peer dst later! */
conn_rtp_dst = &conn_dst->u.rtp;
if (!mgcp_conn_rtp_is_iuup(conn_rtp_dst))
return 0; /* Nothing to do */
if (conn_rtp_dst->iuup.first_rtp_pkt_received) {
/* Note: This could actually happen if INIT is rentransmitted, etc: */
LOG_CONN_RTP(conn_rtp_dst, LOGL_ERROR, "Unexpected: Peer conn received IuUP INIT-ACK but RTP packets already received on this conn!\n");
}
/* We received IuUP parameters on the peer (RNC), Init actively this conn (against CN): */
/* TODO: we still don't send this INIT INDICATION from IuUP FSM! Once we
do, we copy RFCI, etc. and send CONFIGURE.req(init=active) to
conn_rtp_dst->iuup.iui */
return rc;
}
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;
}
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;
}
static int _conn_iuup_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
{
struct mgcp_conn_rtp *conn_src_rtp = NULL, *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;
struct osmo_sockaddr from_addr = {0}; /* FIXME: what to do with this one? */
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 = 0,
.payload_type = conn_rtp_dst->end.codec->payload_type,
.marker = 0,
.sequence = 0,
.timestamp = 0,
.ssrc = 0
};
/* TODO: mgcp_send_rtp() expects msg to have OSMO_RTP_MSG_CTX filled */
// struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
// how to handle an early init packet which need to be answered correct! */
/* The destination of the destination conn is the source conn, right? */
conn_src = _find_dst_conn(conn_dst);
if (conn_src)
conn_src_rtp = &conn_src->u.rtp;
else
conn_src_rtp = &conn_dst->u.rtp;
/* FIXME: set from_addr = NULL */
return mgcp_send(conn_dst->endp, 1,
&from_addr, msg, conn_src_rtp, conn_rtp_dst);
}
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);
return 0;
}
void mgcp_conn_iuup_cleanup(struct mgcp_conn_rtp *conn_rtp)
{
osmo_iuup_instance_free(conn_rtp->iuup.iui);
conn_rtp->iuup.iui = NULL;
}
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;
struct mgcp_conn *conn_dst;
int rc = 0;
bool force_output_enabled = false;
int prev_output_enabled;
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.active_init && !conn_rtp_src->iuup.first_rtp_pkt_received) {
conn_rtp_src->iuup.first_rtp_pkt_received = true;
/* We received the first message without sending any, the peer is the active side (RNC). */
conn_dst = _find_dst_conn(conn_rtp_src->conn);
if (conn_dst && mgcp_conn_rtp_is_iuup(&conn_dst->u.rtp) &&
!conn_dst->u.rtp.iuup.first_rtp_pkt_received) {
conn_dst->u.rtp.iuup.active_init = true;
/* TODO: start Init as active there */
}
rc = _conn_iuup_configure_as_passive(conn_rtp_src, msg);
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 = 1;
force_output_enabled = true;
}
rc = _conn_iuup_rtp_pl_up(conn_rtp_src, msg);
if (force_output_enabled)
conn_rtp_src->end.output_enabled = prev_output_enabled;
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;
struct amr_hdr *amr_hdr;
int rc = -1;
int iuup_length = 0;
/* 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);
/* FIXME: We probably need transcoding here?! Or at least look up AMR modes and translate to related RFCI */
irp->u.data.frame_nr = htons(rtph->sequence) % 16;
if (msgb_length(msg) < (sizeof(struct amr_hdr))) {
return -1;
}
amr_hdr = (struct amr_hdr *) msgb_data(msg);
if (mgcp_codec_amr_is_octet_aligned(conn_src_rtp->end.codec)) {
/* FIXME: CMR handling & multiple frames handling */
irp->u.data.fqc = IUUP_FQC_FRAME_GOOD;
irp->u.data.rfci = amr_hdr->ft < 8 ? 0 : 1;
msgb_pull(msg, 2);
} else {
irp->u.data.fqc = amr_hdr->q;
irp->u.data.rfci = amr_hdr->ft < 8 ? 0 : 1;
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, "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, "Failed Tx RTP payload down the IuUP layer\n");
return rc;
}
return rc;
}

View File

@@ -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);
}

View File

@@ -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
@@ -768,25 +768,6 @@ static bool amr_oa_bwe_convert_indicated(struct mgcp_rtp_codec *codec)
}
/* Return whether an RTP packet with AMR payload is in octet-aligned mode.
* Return 0 if in bandwidth-efficient mode, 1 for octet-aligned mode, and negative if the RTP data is invalid. */
static int amr_oa_check(char *data, int len)
{
struct rtp_hdr *rtp_hdr;
unsigned int payload_len;
if (len < sizeof(struct rtp_hdr))
return -EINVAL;
rtp_hdr = (struct rtp_hdr *)data;
payload_len = len - sizeof(struct rtp_hdr);
if (payload_len < sizeof(struct amr_hdr))
return -EINVAL;
return osmo_amr_is_oa(rtp_hdr->data, payload_len) ? 1 : 0;
}
/* 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)
@@ -975,7 +956,8 @@ 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. */
/* FIXME UPDATE: we can now apply more checks based on mgcp_conn_rtp_is_iuup(conn_src) */
return 0;
}
@@ -1013,6 +995,11 @@ 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:
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);
}
/* If the data has not been handled/forwarded until here, it will
@@ -1139,10 +1126,12 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
* should not occur if transcoding is consequently avoided. Until
* we have transcoding support in osmo-mgw we can not resolve this. */
if (is_rtp) {
rc = mgcp_patch_pt(conn_src, conn_dst, msg);
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"can not patch PT because no suitable egress codec was found.\n");
if (conn_dst->type != MGCP_RTP_IUUP) {
rc = mgcp_patch_pt(conn_src, conn_dst, msg);
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"can not patch PT because no suitable egress codec was found.\n");
}
}
}
@@ -1185,7 +1174,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) {
@@ -1214,25 +1205,6 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
forward_data(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;
data[2] = 0x09; /* Patch CRC Header to adapt to new header above */
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));
@@ -1292,7 +1264,6 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
* destination connection is known the RTP packet is sent via
* the destination connection. */
/* Check if the connection is in loopback mode, if yes, just send the
* incoming data back to the origin */
if (conn->mode == MGCP_CONN_LOOPBACK) {
@@ -1318,9 +1289,18 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
osmo_sockaddr_ntop(&from_addr->u.sa, ipbuf),
conn->u.rtp.end.rtp_port);
}
/* If source is IuUP, we need to handle state, forward it rhough specific bridge: */
if (mgcp_conn_rtp_is_iuup(conn_src))
return mgcp_conn_iuup_dispatch_rtp(msg);
return mgcp_send_rtp(conn_src, msg);
}
/* If source is IuUP, we need to handle state, forward it rhough specific bridge: */
if (mgcp_conn_rtp_is_iuup(conn_src))
return mgcp_conn_iuup_dispatch_rtp(msg);
/* Find a destination connection. */
/* NOTE: This code path runs every time an RTP packet is received. The
* function mgcp_find_dst_conn() we use to determine the detination
@@ -1528,18 +1508,6 @@ static int rx_rtp(struct msgb *msg)
mgcp_conn_watchdog_kick(conn_src->conn);
/* If AMR is configured for the ingress connection a conversion of the
* 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)) {
int oa = amr_oa_check((char*)msgb_data(msg), msgb_length(msg));
if (oa < 0)
return -1;
if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned)
return -1;
}
/* Check if the origin of the RTP packet seems plausible */
if (!trunk->rtp_accept_all && check_rtp_origin(conn_src, from_addr))
return -1;

View File

@@ -29,6 +29,7 @@
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <strings.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
@@ -46,6 +47,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. */
@@ -1028,6 +1030,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 && strcasecmp(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);

View File

@@ -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;