mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-10-23 08:12:01 +00:00
Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
306dc09d37 | ||
|
eb282efa67 | ||
|
02d5c8798b | ||
|
efe3f98ec7 | ||
|
3ebf7e2f05 | ||
|
33826246c5 | ||
|
9ef0c11abd | ||
|
109ff5244e | ||
|
4cf5e71a4b | ||
|
8c6f3141e2 | ||
|
12220eea9c | ||
|
536dff1bb8 | ||
|
ee784f9844 |
@@ -195,6 +195,7 @@ AC_OUTPUT(
|
|||||||
tests/atlocal
|
tests/atlocal
|
||||||
tests/mgcp_client/Makefile
|
tests/mgcp_client/Makefile
|
||||||
tests/mgcp/Makefile
|
tests/mgcp/Makefile
|
||||||
|
tests/iuup/Makefile
|
||||||
doc/Makefile
|
doc/Makefile
|
||||||
doc/examples/Makefile
|
doc/examples/Makefile
|
||||||
doc/manuals/Makefile
|
doc/manuals/Makefile
|
||||||
|
@@ -7,4 +7,6 @@ noinst_HEADERS = \
|
|||||||
mgcp_sdp.h \
|
mgcp_sdp.h \
|
||||||
mgcp_codec.h \
|
mgcp_codec.h \
|
||||||
debug.h \
|
debug.h \
|
||||||
|
iuup_cn_node.h \
|
||||||
|
iuup_protocol.h \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
@@ -29,6 +29,7 @@
|
|||||||
/* Debug Areas of the code */
|
/* Debug Areas of the code */
|
||||||
enum {
|
enum {
|
||||||
DRTP,
|
DRTP,
|
||||||
|
DIUUP,
|
||||||
Debug_LastEntry,
|
Debug_LastEntry,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
47
include/osmocom/mgcp/iuup_cn_node.h
Normal file
47
include/osmocom/mgcp/iuup_cn_node.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||||
|
/* IuUP CN node, minimal implementation */
|
||||||
|
|
||||||
|
/* _____IuUP_CN_____
|
||||||
|
* | |
|
||||||
|
* UE <--> RNC --PDU-> osmo_iuup_cn_rx_pdu() -+-> ---+-> rx_payload()
|
||||||
|
* | | |
|
||||||
|
* | <-PDU-- tx_msg() <-------------+-- <-+--- osmo_iuup_cn_tx_payload()
|
||||||
|
* | |
|
||||||
|
* -----------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct msgb;
|
||||||
|
|
||||||
|
typedef int (*osmo_iuup_data_cb_t)(struct msgb *msg, void *node_priv);
|
||||||
|
|
||||||
|
struct osmo_iuup_cn_cfg {
|
||||||
|
void *node_priv;
|
||||||
|
|
||||||
|
/* When the IuUP peer sent a voice packet, the clean RTP without the IuUP header is fed to this
|
||||||
|
* callback. */
|
||||||
|
osmo_iuup_data_cb_t rx_payload;
|
||||||
|
|
||||||
|
/* IuUP handler requests that a PDU shall be sent to the IuUP peer (e.g. the RNC).
|
||||||
|
* It is guaranteed that the msgb->dst pointer is preserved or copied from the msgb that
|
||||||
|
* originated the request. */
|
||||||
|
osmo_iuup_data_cb_t tx_msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct osmo_iuup_cn {
|
||||||
|
struct osmo_iuup_cn_cfg cfg;
|
||||||
|
char *name;
|
||||||
|
uint8_t next_frame_nr;
|
||||||
|
int rtp_payload_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool osmo_iuup_cn_is_iuup_init(struct msgb *msg);
|
||||||
|
|
||||||
|
struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
|
||||||
|
const char *name_fmt, ...);
|
||||||
|
void osmo_iuup_cn_free(struct osmo_iuup_cn *cn);
|
||||||
|
|
||||||
|
int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *payload);
|
||||||
|
|
||||||
|
int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu);
|
117
include/osmocom/mgcp/iuup_protocol.h
Normal file
117
include/osmocom/mgcp/iuup_protocol.h
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||||
|
/* IuUP protocol handling, minimal implementation */
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <osmocom/core/endian.h>
|
||||||
|
#include <osmocom/core/msgb.h>
|
||||||
|
|
||||||
|
#define OSMO_IUUP_HEADROOM 32
|
||||||
|
|
||||||
|
enum osmo_iuup_pdu_type {
|
||||||
|
OSMO_IUUP_PDU_DATA_WITH_CRC = 0,
|
||||||
|
OSMO_IUUP_PDU_CONTROL_PROCEDURE = 14,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum osmo_iuup_acknack {
|
||||||
|
OSMO_IUUP_ACKNACK_PROCEDURE = 0,
|
||||||
|
OSMO_IUUP_ACKNACK_ACK = 1,
|
||||||
|
OSMO_IUUP_ACKNACK_NACK = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum osmo_iuup_procedure {
|
||||||
|
OSMO_IUUP_PROC_INITIALIZATION = 0,
|
||||||
|
OSMO_IUUP_PROC_RATE_CONTROL = 1,
|
||||||
|
OSMO_IUUP_PROC_TIME_ALIGNMENT = 2,
|
||||||
|
OSMO_IUUP_PROC_ERROR_EVENT = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum osmo_iuup_frame_good {
|
||||||
|
OSMO_IUUP_FRAME_GOOD = 0,
|
||||||
|
OSMO_IUUP_FRAME_BAD = 1,
|
||||||
|
OSMO_IUUP_FRAME_BAD_DUE_TO_RADIO = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct osmo_iuup_hdr_ctrl {
|
||||||
|
#if OSMO_IS_BIG_ENDIAN
|
||||||
|
uint8_t pdu_type:4,
|
||||||
|
ack_nack:2,
|
||||||
|
frame_nr:2;
|
||||||
|
uint8_t mode_version:4,
|
||||||
|
procedure:4;
|
||||||
|
uint8_t header_crc:6,
|
||||||
|
payload_crc_hi:2;
|
||||||
|
uint8_t payload_crc_lo;
|
||||||
|
uint8_t payload[0];
|
||||||
|
#elif OSMO_IS_LITTLE_ENDIAN
|
||||||
|
uint8_t frame_nr:2,
|
||||||
|
ack_nack:2,
|
||||||
|
pdu_type:4;
|
||||||
|
uint8_t procedure:4,
|
||||||
|
mode_version:4;
|
||||||
|
uint8_t payload_crc_hi:2,
|
||||||
|
header_crc:6;
|
||||||
|
uint8_t payload_crc_lo;
|
||||||
|
uint8_t payload[0];
|
||||||
|
#endif
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
union osmo_iuup_hdr_ctrl_payload {
|
||||||
|
struct {
|
||||||
|
#if OSMO_IS_BIG_ENDIAN
|
||||||
|
uint8_t spare:3,
|
||||||
|
iptis_present:1,
|
||||||
|
subflows:3,
|
||||||
|
chain:1;
|
||||||
|
#elif OSMO_IS_LITTLE_ENDIAN
|
||||||
|
uint8_t spare:3,
|
||||||
|
iptis_present:1,
|
||||||
|
subflows:3,
|
||||||
|
chain:1;
|
||||||
|
#endif
|
||||||
|
} initialization;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
#if OSMO_IS_BIG_ENDIAN
|
||||||
|
uint8_t error_distance:2,
|
||||||
|
error_cause:6;
|
||||||
|
#elif OSMO_IS_LITTLE_ENDIAN
|
||||||
|
uint8_t error_cause:6,
|
||||||
|
error_distance:2;
|
||||||
|
#endif
|
||||||
|
} error_event;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const struct value_string osmo_iuup_error_cause_names[];
|
||||||
|
static inline const char *osmo_iuup_error_cause_name(uint8_t val)
|
||||||
|
{ return get_value_string(osmo_iuup_error_cause_names, val); }
|
||||||
|
|
||||||
|
struct osmo_iuup_hdr_data {
|
||||||
|
#if OSMO_IS_BIG_ENDIAN
|
||||||
|
uint8_t pdu_type:4,
|
||||||
|
frame_nr:4;
|
||||||
|
uint8_t frame_good:2,
|
||||||
|
rfci:6;
|
||||||
|
uint8_t header_crc:6,
|
||||||
|
payload_crc_hi:2;
|
||||||
|
uint8_t payload_crc_lo;
|
||||||
|
#elif OSMO_IS_LITTLE_ENDIAN
|
||||||
|
uint8_t frame_nr:4,
|
||||||
|
pdu_type:4;
|
||||||
|
uint8_t rfci:6,
|
||||||
|
frame_good:2;
|
||||||
|
uint8_t payload_crc_hi:2,
|
||||||
|
header_crc:6;
|
||||||
|
uint8_t payload_crc_lo;
|
||||||
|
#endif
|
||||||
|
uint8_t payload[0];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
int osmo_iuup_classify(bool log_errors,
|
||||||
|
const char *log_label,
|
||||||
|
struct msgb *pdu,
|
||||||
|
struct osmo_iuup_hdr_ctrl **is_ctrl,
|
||||||
|
struct osmo_iuup_hdr_data **is_data);
|
||||||
|
bool osmo_iuup_is_init(struct msgb *pdu);
|
||||||
|
void osmo_iuup_make_init_ack(struct msgb *ack);
|
||||||
|
void osmo_iuup_set_checksums(uint8_t *iuup_header_and_payload, unsigned int header_and_payload_len);
|
@@ -5,3 +5,5 @@ void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn);
|
|||||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param);
|
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param);
|
||||||
int mgcp_codec_decide(struct mgcp_conn_rtp *conn);
|
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);
|
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);
|
||||||
|
@@ -23,15 +23,27 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <osmocom/core/msgb.h>
|
||||||
|
|
||||||
struct sockaddr_in;
|
struct sockaddr_in;
|
||||||
struct mgcp_conn;
|
struct mgcp_conn;
|
||||||
|
struct mgcp_conn_rtp;
|
||||||
struct mgcp_endpoint;
|
struct mgcp_endpoint;
|
||||||
|
|
||||||
/* Callback type for RTP dispatcher functions
|
struct osmo_rtp_msg_ctx {
|
||||||
(e.g mgcp_dispatch_rtp_bridge_cb, see below) */
|
int proto;
|
||||||
typedef int (*mgcp_dispatch_rtp_cb) (int proto, struct sockaddr_in *addr,
|
struct mgcp_conn_rtp *conn_src;
|
||||||
char *buf, unsigned int buf_size,
|
struct sockaddr_in *from_addr;
|
||||||
struct mgcp_conn *conn);
|
};
|
||||||
|
|
||||||
|
#define OSMO_RTP_MSG_CTX(MSGB) (*(struct osmo_rtp_msg_ctx**)&((MSGB)->dst))
|
||||||
|
|
||||||
|
#define LOG_ENDP(endp, level, fmt, args...) \
|
||||||
|
LOGP(DRTP, level, "%x@ " fmt, ENDPOINT_NUMBER(endp), ## args)
|
||||||
|
|
||||||
|
/* Callback type for RTP dispatcher functions (e.g mgcp_dispatch_rtp_bridge_cb, see below).
|
||||||
|
* The OSMO_RTP_MSG_CTX() should be set appropriately on the msg. */
|
||||||
|
typedef int (*mgcp_dispatch_rtp_cb) (struct msgb *msg);
|
||||||
|
|
||||||
/* Callback type for endpoint specific cleanup actions. This function
|
/* Callback type for endpoint specific cleanup actions. This function
|
||||||
* is automatically executed when a connection is freed (see mgcp_conn_free()
|
* is automatically executed when a connection is freed (see mgcp_conn_free()
|
||||||
|
@@ -37,6 +37,13 @@
|
|||||||
#define CONN_ID_BTS "0"
|
#define CONN_ID_BTS "0"
|
||||||
#define CONN_ID_NET "1"
|
#define CONN_ID_NET "1"
|
||||||
|
|
||||||
|
#define LOG_CONN(conn, level, fmt, args...) \
|
||||||
|
LOGP(DRTP, level, "(%d@ I:%s) " fmt, \
|
||||||
|
ENDPOINT_NUMBER((conn)->endp), (conn)->id, ## args)
|
||||||
|
|
||||||
|
#define LOG_CONN_RTP(conn_rtp, level, fmt, args...) \
|
||||||
|
LOG_CONN(conn_rtp->conn, level, fmt, ## args)
|
||||||
|
|
||||||
enum mgcp_trunk_type {
|
enum mgcp_trunk_type {
|
||||||
MGCP_TRUNK_VIRTUAL,
|
MGCP_TRUNK_VIRTUAL,
|
||||||
MGCP_TRUNK_E1,
|
MGCP_TRUNK_E1,
|
||||||
@@ -204,6 +211,8 @@ struct mgcp_conn_rtp {
|
|||||||
} osmux;
|
} osmux;
|
||||||
|
|
||||||
struct rate_ctr_group *rate_ctr_group;
|
struct rate_ctr_group *rate_ctr_group;
|
||||||
|
|
||||||
|
struct osmo_iuup_cn *iuup;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! Connection type, specifies which member of the union "u" in mgcp_conn
|
/*! Connection type, specifies which member of the union "u" in mgcp_conn
|
||||||
@@ -266,11 +275,10 @@ struct mgcp_parse_data {
|
|||||||
};
|
};
|
||||||
|
|
||||||
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||||
char *buf, int rc, struct mgcp_conn_rtp *conn_src,
|
struct msgb *msg, struct mgcp_conn_rtp *conn_src,
|
||||||
struct mgcp_conn_rtp *conn_dst);
|
struct mgcp_conn_rtp *conn_dst);
|
||||||
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
|
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
|
||||||
int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
|
int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg);
|
||||||
unsigned int buf_size, struct mgcp_conn *conn);
|
|
||||||
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
|
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
|
||||||
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
|
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
|
||||||
struct mgcp_conn_rtp *conn);
|
struct mgcp_conn_rtp *conn);
|
||||||
@@ -350,3 +358,8 @@ LOGP(cat, level, "endpoint:0x%x " fmt, \
|
|||||||
LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
|
LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
|
||||||
(conn)->id, \
|
(conn)->id, \
|
||||||
## args)
|
## args)
|
||||||
|
|
||||||
|
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||||
|
struct mgcp_rtp_state *state,
|
||||||
|
struct mgcp_rtp_end *rtp_end,
|
||||||
|
struct sockaddr_in *addr, struct msgb *msg);
|
||||||
|
@@ -337,48 +337,34 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li
|
|||||||
{
|
{
|
||||||
unsigned int pt;
|
unsigned int pt;
|
||||||
char codec_resp[64];
|
char codec_resp[64];
|
||||||
unsigned int codec;
|
enum mgcp_codecs codec;
|
||||||
|
|
||||||
|
#define A_PTIME "a=ptime:"
|
||||||
|
#define A_RTPMAP "a=rtpmap:"
|
||||||
|
|
||||||
if (strstr(line, "ptime")) {
|
if (osmo_str_startswith(line, A_PTIME)) {
|
||||||
if (sscanf(line, "a=ptime:%u", &r->ptime) != 1)
|
if (sscanf(line, A_PTIME "%u", &r->ptime) != 1) {
|
||||||
goto response_parse_failure_ptime;
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
} else if (strstr(line, "rtpmap")) {
|
"Failed to parse SDP parameter, invalid ptime (%s)\n", line);
|
||||||
if (sscanf(line, "a=rtpmap:%d %63s", &pt, codec_resp) == 2) {
|
return -EINVAL;
|
||||||
/* The MGW may assign an own payload type in the
|
}
|
||||||
* response if the choosen codec falls into the IANA
|
} else if (osmo_str_startswith(line, A_RTPMAP)) {
|
||||||
* assigned dynamic payload type range (96-127).
|
if (sscanf(line, A_RTPMAP "%d %63s", &pt, codec_resp) != 2) {
|
||||||
* Normally the MGW should obey the 3gpp payload type
|
LOGP(DLMGCP, LOGL_ERROR,
|
||||||
* assignments, which are fixed, so we likely wont see
|
"Failed to parse SDP parameter, invalid rtpmap: %s\n", osmo_quote_str(line, -1));
|
||||||
* anything unexpected here. In order to be sure that
|
return -EINVAL;
|
||||||
* we will now check the codec string and if the result
|
}
|
||||||
* does not match to what is IANA / 3gpp assigned, we
|
if (r->ptmap_len >= ARRAY_SIZE(r->ptmap)) {
|
||||||
* will create an entry in the ptmap table so we can
|
LOGP(DLMGCP, LOGL_ERROR, "No more space in ptmap array (len=%u)\n", r->ptmap_len);
|
||||||
* lookup later what has been assigned. */
|
return -ENOSPC;
|
||||||
codec = map_str_to_codec(codec_resp);
|
}
|
||||||
if (codec != pt) {
|
codec = map_str_to_codec(codec_resp);
|
||||||
if (r->ptmap_len < ARRAY_SIZE(r->ptmap)) {
|
r->ptmap[r->ptmap_len].pt = pt;
|
||||||
r->ptmap[r->ptmap_len].pt = pt;
|
r->ptmap[r->ptmap_len].codec = codec;
|
||||||
r->ptmap[r->ptmap_len].codec = codec;
|
r->ptmap_len++;
|
||||||
r->ptmap_len++;
|
|
||||||
} else
|
|
||||||
goto response_parse_failure_rtpmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else
|
|
||||||
goto response_parse_failure_rtpmap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
response_parse_failure_ptime:
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Failed to parse SDP parameter, invalid ptime (%s)\n", line);
|
|
||||||
return -EINVAL;
|
|
||||||
response_parse_failure_rtpmap:
|
|
||||||
LOGP(DLMGCP, LOGL_ERROR,
|
|
||||||
"Failed to parse SDP parameter, invalid rtpmap (%s)\n", line);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse a line like "c=IN IP4 10.11.12.13" */
|
/* Parse a line like "c=IN IP4 10.11.12.13" */
|
||||||
|
@@ -40,4 +40,6 @@ libosmo_mgcp_a_SOURCES = \
|
|||||||
mgcp_conn.c \
|
mgcp_conn.c \
|
||||||
mgcp_stat.c \
|
mgcp_stat.c \
|
||||||
mgcp_endp.c \
|
mgcp_endp.c \
|
||||||
|
iuup_protocol.c \
|
||||||
|
iuup_cn_node.c \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
211
src/libosmo-mgcp/iuup_cn_node.c
Normal file
211
src/libosmo-mgcp/iuup_cn_node.c
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||||
|
/* IuUP Core Network side protocol handling, minimal implementation */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* Author: Neels Hofmeyr <neels@hofmeyr.de>
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <talloc.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/utils.h>
|
||||||
|
#include <osmocom/core/logging.h>
|
||||||
|
#include <osmocom/core/msgb.h>
|
||||||
|
|
||||||
|
#include <osmocom/netif/rtp.h>
|
||||||
|
|
||||||
|
#include <osmocom/mgcp/iuup_cn_node.h>
|
||||||
|
#include <osmocom/mgcp/iuup_protocol.h>
|
||||||
|
|
||||||
|
#include <osmocom/mgcp/debug.h>
|
||||||
|
|
||||||
|
#define LOG_IUUP_CN(cn, level, fmt, args...) \
|
||||||
|
LOGP(DIUUP, level, "(%s) " fmt, (cn)->name, ## args)
|
||||||
|
|
||||||
|
struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
|
||||||
|
const char *name_fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
struct osmo_iuup_cn *cn = talloc_zero(ctx, struct osmo_iuup_cn);
|
||||||
|
OSMO_ASSERT(cn);
|
||||||
|
|
||||||
|
cn->cfg = *cfg;
|
||||||
|
|
||||||
|
if (!name_fmt)
|
||||||
|
name_fmt = "-";
|
||||||
|
|
||||||
|
va_start(ap, name_fmt);
|
||||||
|
cn->name = talloc_vasprintf(cn, name_fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
LOGP(DIUUP, LOGL_INFO, "(%s) Initializing IuUP node\n", cn->name);
|
||||||
|
|
||||||
|
if (!osmo_identifier_valid(cn->name)) {
|
||||||
|
LOGP(DIUUP, LOGL_ERROR, "Attempting to set illegal id for IuUP CN instance: %s\n",
|
||||||
|
osmo_quote_str(cn->name, -1));
|
||||||
|
talloc_free(cn);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void osmo_iuup_cn_free(struct osmo_iuup_cn *cn)
|
||||||
|
{
|
||||||
|
talloc_free(cn);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rx_data(struct osmo_iuup_cn *cn, struct msgb *pdu,
|
||||||
|
struct osmo_iuup_hdr_data *hdr)
|
||||||
|
{
|
||||||
|
/* Remove the IuUP bit from the middle of the buffer by writing the RTP header forward. */
|
||||||
|
/* And append AMR 12.2k header "0xf03c". - AD HOC fix */
|
||||||
|
unsigned int pre_hdr_len = ((uint8_t*)hdr) - pdu->data;
|
||||||
|
memmove(pdu->data + sizeof(*hdr) - 2, pdu->data, pre_hdr_len);
|
||||||
|
((uint8_t*)hdr)[2] = 0xf0;
|
||||||
|
((uint8_t*)hdr)[3] = 0x3c;
|
||||||
|
msgb_pull(pdu, sizeof(*hdr) - 2);
|
||||||
|
|
||||||
|
LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP stripping IuUP header from RTP data\n", cn->name);
|
||||||
|
cn->cfg.rx_payload(pdu, cn->cfg.node_priv);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tx_init_ack(struct osmo_iuup_cn *cn, struct msgb *src_pdu)
|
||||||
|
{
|
||||||
|
/* Send Initialization Ack PDU back to the sender */
|
||||||
|
int rc;
|
||||||
|
struct msgb *ack = msgb_alloc(4096, "IuUP Initialization Ack");
|
||||||
|
OSMO_ASSERT(ack);
|
||||||
|
ack->dst = src_pdu->dst;
|
||||||
|
|
||||||
|
/* Just copy the RTP header that was sent... TODO: tweak some RTP values?? */
|
||||||
|
memcpy(msgb_put(ack, sizeof(struct rtp_hdr)), src_pdu->data, sizeof(struct rtp_hdr));
|
||||||
|
|
||||||
|
osmo_iuup_make_init_ack(ack);
|
||||||
|
|
||||||
|
LOGP(DIUUP, LOGL_DEBUG, "(%s) Sending Initialization ACK %p\n", cn->name, cn->cfg.node_priv);
|
||||||
|
rc = cn->cfg.tx_msg(ack, cn->cfg.node_priv);
|
||||||
|
msgb_free(ack);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rx_control(struct osmo_iuup_cn *cn, struct msgb *pdu,
|
||||||
|
struct osmo_iuup_hdr_ctrl *hdr)
|
||||||
|
{
|
||||||
|
switch (hdr->procedure) {
|
||||||
|
case OSMO_IUUP_PROC_INITIALIZATION:
|
||||||
|
switch (hdr->ack_nack) {
|
||||||
|
case OSMO_IUUP_ACKNACK_PROCEDURE:
|
||||||
|
LOGP(DIUUP, LOGL_INFO, "(%s) Rx IuUP Initialization, sending ACK\n", cn->name);
|
||||||
|
cn->rtp_payload_type = ((struct rtp_hdr*)pdu->data)->payload_type;
|
||||||
|
return tx_init_ack(cn, pdu);
|
||||||
|
|
||||||
|
default:
|
||||||
|
LOGP(DIUUP, LOGL_DEBUG, "(%s) Rx IuUP Initialization, unhandled ack_nack = %d\n",
|
||||||
|
cn->name, hdr->ack_nack);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Continue to log "unexpected procedure" below. */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OSMO_IUUP_PROC_ERROR_EVENT:
|
||||||
|
{
|
||||||
|
union osmo_iuup_hdr_ctrl_payload *p = (void*)hdr->payload;
|
||||||
|
LOGP(DIUUP, LOGL_ERROR,
|
||||||
|
"(%s) Rx IuUP Error Event: distance=%u, cause=%u=\"%s\"\n",
|
||||||
|
cn->name, p->error_event.error_distance, p->error_event.error_cause,
|
||||||
|
osmo_iuup_error_cause_name(p->error_event.error_cause));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
LOG_IUUP_CN(cn, LOGL_ERROR,
|
||||||
|
"Rx control PDU with unexpected procedure: 0x%x acknack=0x%x\n",
|
||||||
|
hdr->procedure, hdr->ack_nack);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Feed a received PDU to the IuUP CN node. This function takes ownership of the msgb, it must not be
|
||||||
|
* freed by the caller. */
|
||||||
|
int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu)
|
||||||
|
{
|
||||||
|
struct osmo_iuup_hdr_ctrl *is_ctrl;
|
||||||
|
struct osmo_iuup_hdr_data *is_data;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = osmo_iuup_classify(true, cn->name, pdu, &is_ctrl, &is_data);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
if (is_ctrl)
|
||||||
|
return rx_control(cn, pdu, is_ctrl);
|
||||||
|
if (is_data)
|
||||||
|
return rx_data(cn, pdu, is_data);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t next_frame_nr(struct osmo_iuup_cn *cn)
|
||||||
|
{
|
||||||
|
uint8_t frame_nr = cn->next_frame_nr;
|
||||||
|
cn->next_frame_nr = (frame_nr + 1) & 0x0f;
|
||||||
|
return frame_nr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send this RTP packet to the IuUP peer: add IuUP header and call the tx_msg() to transmit the resulting
|
||||||
|
* message to the IuUP peer.
|
||||||
|
* Returns 0 on success, negative on error. */
|
||||||
|
int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *pdu)
|
||||||
|
{
|
||||||
|
struct rtp_hdr *rtp_was, *rtp;
|
||||||
|
struct osmo_iuup_hdr_data *iuup_hdr;
|
||||||
|
|
||||||
|
/* Splice an IuUP header in between RTP header and payload data */
|
||||||
|
rtp_was = (void*)pdu->data;
|
||||||
|
|
||||||
|
/* copy the RTP header part backwards by the size needed for the IuUP header */
|
||||||
|
/* also strips 2 bytes from the front of RTP payload - AMR header - AD HOC fix */
|
||||||
|
rtp = (void*)msgb_push(pdu, sizeof(*iuup_hdr) - 2);
|
||||||
|
memmove(rtp, rtp_was, sizeof(*rtp));
|
||||||
|
|
||||||
|
/* The IuUP side negotiated a payload type number to use during Initialization. The RTP packet going to the IuUP
|
||||||
|
* peer should reflect this payload_type. This should already have happened in mgcp_patch_pt(), but can't hurt
|
||||||
|
* to patch over it again. */
|
||||||
|
rtp->payload_type = cn->rtp_payload_type;
|
||||||
|
|
||||||
|
iuup_hdr = (void*)rtp->data;
|
||||||
|
|
||||||
|
*iuup_hdr = (struct osmo_iuup_hdr_data){
|
||||||
|
.pdu_type = OSMO_IUUP_PDU_DATA_WITH_CRC,
|
||||||
|
.frame_nr = next_frame_nr(cn),
|
||||||
|
.frame_good = OSMO_IUUP_FRAME_GOOD,
|
||||||
|
};
|
||||||
|
|
||||||
|
osmo_iuup_set_checksums((uint8_t*)iuup_hdr, pdu->tail - (uint8_t*)iuup_hdr);
|
||||||
|
LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP inserting IuUP header in RTP data (frame nr %u)\n",
|
||||||
|
cn->name, iuup_hdr->frame_nr);
|
||||||
|
|
||||||
|
return cn->cfg.tx_msg(pdu, cn->cfg.node_priv);
|
||||||
|
}
|
286
src/libosmo-mgcp/iuup_protocol.c
Normal file
286
src/libosmo-mgcp/iuup_protocol.c
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||||
|
/* IuUP Core Network side protocol, minimal implementation */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* Author: Neels Hofmeyr <neels@hofmeyr.de>
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <osmocom/mgcp/iuup_protocol.h>
|
||||||
|
#include <osmocom/mgcp/debug.h>
|
||||||
|
#include <osmocom/netif/rtp.h>
|
||||||
|
|
||||||
|
/* Calculating two bytes of CRC is ok to do by a loop */
|
||||||
|
static uint8_t header_crc6(const uint8_t *hdr)
|
||||||
|
{
|
||||||
|
int bit;
|
||||||
|
/* Polynomial: D^6 + D^5 + D^3 + D^2 + D^1 + 1
|
||||||
|
* that's 1101111 or 0x6f;
|
||||||
|
* align its lowest bit with a uint16_t's highest bit: */
|
||||||
|
uint32_t polynomial = 0x6f << 15; // 00110111 10000000 00000000
|
||||||
|
uint32_t remainder = ( ((uint32_t)hdr[0]) << 8 | hdr[1] ) << 6;
|
||||||
|
|
||||||
|
for (bit = 15; bit >= 0; bit--)
|
||||||
|
{
|
||||||
|
if (remainder & (0x40 << bit))
|
||||||
|
remainder ^= polynomial;
|
||||||
|
polynomial >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return remainder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Charles Michael Heard's CRC-10 code, from
|
||||||
|
*
|
||||||
|
* http://web.archive.org/web/20061005231950/http://cell-relay.indiana.edu/cell-relay/publications/software/CRC/crc10.html
|
||||||
|
*
|
||||||
|
* with the CRC table initialized with values computed by
|
||||||
|
* his "gen_byte_crc10_table()" routine, rather than by calling that
|
||||||
|
* routine at run time, and with various data type cleanups.
|
||||||
|
*/
|
||||||
|
static const uint16_t byte_crc10_table[256] = {
|
||||||
|
0x0000, 0x0233, 0x0255, 0x0066, 0x0299, 0x00aa, 0x00cc, 0x02ff,
|
||||||
|
0x0301, 0x0132, 0x0154, 0x0367, 0x0198, 0x03ab, 0x03cd, 0x01fe,
|
||||||
|
0x0031, 0x0202, 0x0264, 0x0057, 0x02a8, 0x009b, 0x00fd, 0x02ce,
|
||||||
|
0x0330, 0x0103, 0x0165, 0x0356, 0x01a9, 0x039a, 0x03fc, 0x01cf,
|
||||||
|
0x0062, 0x0251, 0x0237, 0x0004, 0x02fb, 0x00c8, 0x00ae, 0x029d,
|
||||||
|
0x0363, 0x0150, 0x0136, 0x0305, 0x01fa, 0x03c9, 0x03af, 0x019c,
|
||||||
|
0x0053, 0x0260, 0x0206, 0x0035, 0x02ca, 0x00f9, 0x009f, 0x02ac,
|
||||||
|
0x0352, 0x0161, 0x0107, 0x0334, 0x01cb, 0x03f8, 0x039e, 0x01ad,
|
||||||
|
0x00c4, 0x02f7, 0x0291, 0x00a2, 0x025d, 0x006e, 0x0008, 0x023b,
|
||||||
|
0x03c5, 0x01f6, 0x0190, 0x03a3, 0x015c, 0x036f, 0x0309, 0x013a,
|
||||||
|
0x00f5, 0x02c6, 0x02a0, 0x0093, 0x026c, 0x005f, 0x0039, 0x020a,
|
||||||
|
0x03f4, 0x01c7, 0x01a1, 0x0392, 0x016d, 0x035e, 0x0338, 0x010b,
|
||||||
|
0x00a6, 0x0295, 0x02f3, 0x00c0, 0x023f, 0x000c, 0x006a, 0x0259,
|
||||||
|
0x03a7, 0x0194, 0x01f2, 0x03c1, 0x013e, 0x030d, 0x036b, 0x0158,
|
||||||
|
0x0097, 0x02a4, 0x02c2, 0x00f1, 0x020e, 0x003d, 0x005b, 0x0268,
|
||||||
|
0x0396, 0x01a5, 0x01c3, 0x03f0, 0x010f, 0x033c, 0x035a, 0x0169,
|
||||||
|
0x0188, 0x03bb, 0x03dd, 0x01ee, 0x0311, 0x0122, 0x0144, 0x0377,
|
||||||
|
0x0289, 0x00ba, 0x00dc, 0x02ef, 0x0010, 0x0223, 0x0245, 0x0076,
|
||||||
|
0x01b9, 0x038a, 0x03ec, 0x01df, 0x0320, 0x0113, 0x0175, 0x0346,
|
||||||
|
0x02b8, 0x008b, 0x00ed, 0x02de, 0x0021, 0x0212, 0x0274, 0x0047,
|
||||||
|
0x01ea, 0x03d9, 0x03bf, 0x018c, 0x0373, 0x0140, 0x0126, 0x0315,
|
||||||
|
0x02eb, 0x00d8, 0x00be, 0x028d, 0x0072, 0x0241, 0x0227, 0x0014,
|
||||||
|
0x01db, 0x03e8, 0x038e, 0x01bd, 0x0342, 0x0171, 0x0117, 0x0324,
|
||||||
|
0x02da, 0x00e9, 0x008f, 0x02bc, 0x0043, 0x0270, 0x0216, 0x0025,
|
||||||
|
0x014c, 0x037f, 0x0319, 0x012a, 0x03d5, 0x01e6, 0x0180, 0x03b3,
|
||||||
|
0x024d, 0x007e, 0x0018, 0x022b, 0x00d4, 0x02e7, 0x0281, 0x00b2,
|
||||||
|
0x017d, 0x034e, 0x0328, 0x011b, 0x03e4, 0x01d7, 0x01b1, 0x0382,
|
||||||
|
0x027c, 0x004f, 0x0029, 0x021a, 0x00e5, 0x02d6, 0x02b0, 0x0083,
|
||||||
|
0x012e, 0x031d, 0x037b, 0x0148, 0x03b7, 0x0184, 0x01e2, 0x03d1,
|
||||||
|
0x022f, 0x001c, 0x007a, 0x0249, 0x00b6, 0x0285, 0x02e3, 0x00d0,
|
||||||
|
0x011f, 0x032c, 0x034a, 0x0179, 0x0386, 0x01b5, 0x01d3, 0x03e0,
|
||||||
|
0x021e, 0x002d, 0x004b, 0x0278, 0x0087, 0x02b4, 0x02d2, 0x00e1
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint16_t crc10(uint16_t crc10_accum, const uint8_t *payload, unsigned int payload_len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < payload_len; i++) {
|
||||||
|
crc10_accum = ((crc10_accum << 8) & 0x300)
|
||||||
|
^ byte_crc10_table[(crc10_accum >> 2) & 0xff]
|
||||||
|
^ payload[i];
|
||||||
|
}
|
||||||
|
return crc10_accum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When a payload of a multiple of bytes has run through, we need to still feed 10 bits of zeros into the
|
||||||
|
* CRC10 to get the payload's checksum result that we can send to a peer. That can't be done with above
|
||||||
|
* table, because it acts as if full 16 bits are fed. This stops after 10 bits. */
|
||||||
|
static uint16_t crc10_remainder(uint16_t crc10_accum)
|
||||||
|
{
|
||||||
|
int bit;
|
||||||
|
/* Polynomial: D^10 + D^9 + D^5 + D^4 + D^1 + 1
|
||||||
|
* that's 11000110011 or 0x633;
|
||||||
|
* align its lowest bit with a 10bit value's highest bit: */
|
||||||
|
uint32_t polynomial = 0x633 << 9; // 1100 01100110 00000000
|
||||||
|
uint32_t remainder = ((uint32_t)crc10_accum) << 10;
|
||||||
|
|
||||||
|
/* Run on 10 bits */
|
||||||
|
for (bit = 9; bit >= 0; bit--)
|
||||||
|
{
|
||||||
|
if (remainder & ((1 << 10) << bit))
|
||||||
|
remainder ^= polynomial;
|
||||||
|
polynomial >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return remainder & 0x3ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t payload_crc10(const uint8_t *payload, unsigned int payload_len)
|
||||||
|
{
|
||||||
|
uint16_t crc10_accum = crc10(0, payload, payload_len);
|
||||||
|
return crc10_remainder(crc10_accum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Given an IuUP PDU data block, write the correct header and payload CRC checksums at the right places.
|
||||||
|
*/
|
||||||
|
void osmo_iuup_set_checksums(uint8_t *iuup_header_and_payload, unsigned int header_and_payload_len)
|
||||||
|
{
|
||||||
|
/* For both data and ctrl, the checksums and payload are at the same offset */
|
||||||
|
struct osmo_iuup_hdr_data *hdr = (void*)iuup_header_and_payload;
|
||||||
|
uint16_t crc;
|
||||||
|
unsigned int payload_len;
|
||||||
|
|
||||||
|
hdr->header_crc = header_crc6(iuup_header_and_payload);
|
||||||
|
|
||||||
|
payload_len = iuup_header_and_payload + header_and_payload_len - hdr->payload;
|
||||||
|
crc = payload_crc10(hdr->payload, payload_len);
|
||||||
|
hdr->payload_crc_hi = (crc >> 8) & 0x3;
|
||||||
|
hdr->payload_crc_lo = crc & 0xff;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate minimum message sizes, IuUP PDU type, header- and payload checksums. If it is a Control
|
||||||
|
* Procedure PDU, return the header position in is_ctrl, if it is a Data PDU, return the header position
|
||||||
|
* in is_data. If log_errors is true, log on DIUUP with the given log label for context. Return NULL in
|
||||||
|
* both is_ctrl and is_data, and return a negative error code if the PDU could not be identified as a
|
||||||
|
* valid RTP PDU containing an IuUP part. */
|
||||||
|
int osmo_iuup_classify(bool log_errors,
|
||||||
|
const char *log_label,
|
||||||
|
struct msgb *pdu,
|
||||||
|
struct osmo_iuup_hdr_ctrl **is_ctrl,
|
||||||
|
struct osmo_iuup_hdr_data **is_data)
|
||||||
|
{
|
||||||
|
struct rtp_hdr *rtp = (void*)pdu->data;
|
||||||
|
struct osmo_iuup_hdr_ctrl *hdr = (void*)rtp->data;
|
||||||
|
unsigned int payload_len;
|
||||||
|
uint16_t crc_calculated;
|
||||||
|
uint16_t crc_from_peer;
|
||||||
|
|
||||||
|
#define ERR(fmt, args...) do { \
|
||||||
|
if (log_errors) \
|
||||||
|
LOGP(DIUUP, LOGL_ERROR, "(%s) " fmt, log_label? : "-", ## args); \
|
||||||
|
return -EINVAL; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
if (is_ctrl)
|
||||||
|
*is_ctrl = NULL;
|
||||||
|
if (is_data)
|
||||||
|
*is_data = NULL;
|
||||||
|
|
||||||
|
/* We need at least a header of 4 bytes. The osmo_iuup_hdr_ctrl already includes a byte of
|
||||||
|
* payload, so use osmo_iuup_hdr_data to check the minimum here. */
|
||||||
|
if (pdu->len < (sizeof(*rtp) + sizeof(struct osmo_iuup_hdr_data)))
|
||||||
|
ERR("IuUP PDU too short: %u\n", pdu->len);
|
||||||
|
|
||||||
|
/* Let's not validate checksums if the header type isn't sane */
|
||||||
|
switch (hdr->pdu_type) {
|
||||||
|
case OSMO_IUUP_PDU_DATA_WITH_CRC:
|
||||||
|
/* If the caller isn't interested in data PDUs, cut short here. */
|
||||||
|
if (!is_data)
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
case OSMO_IUUP_PDU_CONTROL_PROCEDURE:
|
||||||
|
/* If the caller isn't interested in control PDUs, cut short here. */
|
||||||
|
if (!is_ctrl)
|
||||||
|
return 0;
|
||||||
|
if (pdu->len < (sizeof(*rtp) + sizeof(struct osmo_iuup_hdr_ctrl)))
|
||||||
|
ERR("IuUP control PDU too short: %u\n", pdu->len);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ERR("IuUP with invalid type: %u\n", hdr->pdu_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For both data and ctrl, the checksums and payload are at the same offset */
|
||||||
|
|
||||||
|
crc_calculated = header_crc6((uint8_t*)hdr);
|
||||||
|
if (crc_calculated != hdr->header_crc)
|
||||||
|
ERR("IuUP PDU with invalid header CRC (peer sent 0x%x, calculated 0x%x)\n",
|
||||||
|
hdr->header_crc, crc_calculated);
|
||||||
|
|
||||||
|
payload_len = pdu->tail - hdr->payload;
|
||||||
|
crc_calculated = payload_crc10(hdr->payload, payload_len);
|
||||||
|
crc_from_peer = (((uint16_t)hdr->payload_crc_hi) << 8) | hdr->payload_crc_lo;
|
||||||
|
if (crc_from_peer != crc_calculated)
|
||||||
|
ERR("IuUP PDU with invalid payload CRC (peer sent 0x%x, calculated 0x%x)\n",
|
||||||
|
crc_from_peer, crc_calculated);
|
||||||
|
|
||||||
|
switch (hdr->pdu_type) {
|
||||||
|
case OSMO_IUUP_PDU_DATA_WITH_CRC:
|
||||||
|
if (is_data)
|
||||||
|
*is_data = (void*)hdr;
|
||||||
|
return 0;
|
||||||
|
case OSMO_IUUP_PDU_CONTROL_PROCEDURE:
|
||||||
|
if (is_ctrl)
|
||||||
|
*is_ctrl = hdr;
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
ERR("IuUP with invalid type: %u\n", hdr->pdu_type);
|
||||||
|
}
|
||||||
|
#undef ERR
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return true if this RTP packet contains an IuUP Initialization header (detect IuUP peer). */
|
||||||
|
bool osmo_iuup_is_init(struct msgb *pdu)
|
||||||
|
{
|
||||||
|
struct osmo_iuup_hdr_ctrl *is_ctrl;
|
||||||
|
osmo_iuup_classify(false, NULL, pdu, &is_ctrl, NULL);
|
||||||
|
return is_ctrl
|
||||||
|
&& is_ctrl->procedure == OSMO_IUUP_PROC_INITIALIZATION
|
||||||
|
&& is_ctrl->ack_nack == OSMO_IUUP_ACKNACK_PROCEDURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Append an IuUP Initialization ACK message */
|
||||||
|
void osmo_iuup_make_init_ack(struct msgb *ack)
|
||||||
|
{
|
||||||
|
/* Send Initialization Ack PDU back to the sender */
|
||||||
|
struct osmo_iuup_hdr_ctrl *hdr;
|
||||||
|
OSMO_ASSERT(ack);
|
||||||
|
|
||||||
|
hdr = (void*)msgb_put(ack, sizeof(*hdr));
|
||||||
|
|
||||||
|
*hdr = (struct osmo_iuup_hdr_ctrl){
|
||||||
|
.pdu_type = OSMO_IUUP_PDU_CONTROL_PROCEDURE,
|
||||||
|
.ack_nack = OSMO_IUUP_ACKNACK_ACK,
|
||||||
|
.procedure = OSMO_IUUP_PROC_INITIALIZATION,
|
||||||
|
};
|
||||||
|
|
||||||
|
osmo_iuup_set_checksums((uint8_t*)hdr, sizeof(*hdr));
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct value_string osmo_iuup_error_cause_names[] = {
|
||||||
|
{ 0, "CRC error of frame header" },
|
||||||
|
{ 1, "CRC error of frame payload" },
|
||||||
|
{ 2, "Unexpected frame number" },
|
||||||
|
{ 3, "Frame loss" },
|
||||||
|
{ 4, "PDU type unknown" },
|
||||||
|
{ 5, "Unknown procedure" },
|
||||||
|
{ 6, "Unknown reserved value" },
|
||||||
|
{ 7, "Unknown field" },
|
||||||
|
{ 8, "Frame too short" },
|
||||||
|
{ 9, "Missing fields" },
|
||||||
|
{ 16, "Unexpected PDU type" },
|
||||||
|
{ 17, "spare" },
|
||||||
|
{ 18, "Unexpected procedure" },
|
||||||
|
{ 19, "Unexpected RFCI" },
|
||||||
|
{ 20, "Unexpected value" },
|
||||||
|
{ 42, "Initialisation failure" },
|
||||||
|
{ 43, "Initialisation failure (network error, timer expiry)" },
|
||||||
|
{ 44, "Initialisation failure (Iu UP function error, repeated NACK)" },
|
||||||
|
{ 45, "Rate control failure" },
|
||||||
|
{ 46, "Error event failure" },
|
||||||
|
{ 47, "Time Alignment not supported" },
|
||||||
|
{ 48, "Requested Time Alignment not possible" },
|
||||||
|
{ 49, "Iu UP Mode version not supported" },
|
||||||
|
{}
|
||||||
|
};
|
@@ -76,36 +76,61 @@ void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Initalize or reset codec information with default data. */
|
/* Initalize or reset codec information with default data. */
|
||||||
void codec_init(struct mgcp_rtp_codec *codec)
|
static void codec_init(struct mgcp_rtp_codec *codec)
|
||||||
|
{
|
||||||
|
*codec = (struct mgcp_rtp_codec){
|
||||||
|
.payload_type = -1,
|
||||||
|
.frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM,
|
||||||
|
.frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN,
|
||||||
|
.rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE,
|
||||||
|
.channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static void codec_free(struct mgcp_rtp_codec *codec)
|
||||||
{
|
{
|
||||||
if (codec->subtype_name)
|
if (codec->subtype_name)
|
||||||
talloc_free(codec->subtype_name);
|
talloc_free(codec->subtype_name);
|
||||||
if (codec->audio_name)
|
if (codec->audio_name)
|
||||||
talloc_free(codec->audio_name);
|
talloc_free(codec->audio_name);
|
||||||
memset(codec, 0, sizeof(*codec));
|
*codec = (struct mgcp_rtp_codec){};
|
||||||
codec->payload_type = -1;
|
|
||||||
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
|
|
||||||
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
|
|
||||||
codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
|
|
||||||
codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Initalize or reset codec information with default data.
|
/*! Initalize or reset codec information with default data.
|
||||||
* \param[out] conn related rtp-connection. */
|
* \param[out] conn related rtp-connection. */
|
||||||
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
|
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
|
||||||
{
|
{
|
||||||
memset(conn->end.codecs, 0, sizeof(conn->end.codecs));
|
int i;
|
||||||
|
for (i = 0; i < conn->end.codecs_assigned; i++)
|
||||||
|
codec_free(&conn->end.codecs[i]);
|
||||||
conn->end.codecs_assigned = 0;
|
conn->end.codecs_assigned = 0;
|
||||||
conn->end.codec = NULL;
|
conn->end.codec = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set members of struct mgcp_rtp_codec, extrapolate in missing information. Param audio_name is expected in uppercase. */
|
/*! Add codec configuration depending on payload type and/or codec name. This
|
||||||
static int codec_set(void *ctx, struct mgcp_rtp_codec *codec, int payload_type, const char *audio_name,
|
* function uses the input parameters to extrapolate the full codec information.
|
||||||
unsigned int pt_offset, const struct mgcp_codec_param *param)
|
* \param[out] codec configuration (caller provided memory).
|
||||||
|
* \param[out] conn related rtp-connection.
|
||||||
|
* \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
|
||||||
|
* \param[in] audio_name audio codec name, in uppercase (e.g. "GSM/8000/1").
|
||||||
|
* \param[in] param optional codec parameters (set to NULL when unused).
|
||||||
|
* \returns 0 on success, -EINVAL on failure. */
|
||||||
|
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
|
||||||
{
|
{
|
||||||
int rate;
|
int rate;
|
||||||
int channels;
|
int channels;
|
||||||
char audio_codec[64];
|
char audio_codec[64];
|
||||||
|
struct mgcp_rtp_codec *codec;
|
||||||
|
unsigned int pt_offset = conn->end.codecs_assigned;
|
||||||
|
void *ctx = conn->conn;
|
||||||
|
|
||||||
|
/* The amount of codecs we can store is limited, make sure we do not
|
||||||
|
* overrun this limit. */
|
||||||
|
if (conn->end.codecs_assigned >= MGCP_MAX_CODECS)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* First unused entry */
|
||||||
|
codec = &conn->end.codecs[conn->end.codecs_assigned];
|
||||||
|
|
||||||
/* Initalize the codec struct with some default data to begin with */
|
/* Initalize the codec struct with some default data to begin with */
|
||||||
codec_init(codec);
|
codec_init(codec);
|
||||||
@@ -113,12 +138,13 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec, int payload_type,
|
|||||||
if (payload_type != PTYPE_UNDEFINED) {
|
if (payload_type != PTYPE_UNDEFINED) {
|
||||||
/* Make sure we do not get any reserved or undefined type numbers */
|
/* Make sure we do not get any reserved or undefined type numbers */
|
||||||
/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
|
/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
|
||||||
if (payload_type == 1 || payload_type == 2 || payload_type == 19)
|
if ((payload_type == 1 || payload_type == 2 || payload_type == 19)
|
||||||
goto error;
|
|| (payload_type >= 72 && payload_type <= 76)
|
||||||
if (payload_type >= 72 && payload_type <= 76)
|
|| (payload_type >= 127)) {
|
||||||
goto error;
|
LOGP(DLMGCP, LOGL_ERROR, "Cannot add codec, payload type number %d is reserved\n",
|
||||||
if (payload_type >= 127)
|
payload_type);
|
||||||
goto error;
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
codec->payload_type = payload_type;
|
codec->payload_type = payload_type;
|
||||||
}
|
}
|
||||||
@@ -144,6 +170,8 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec, int payload_type,
|
|||||||
/* The given payload type is not known to us, or it
|
/* The given payload type is not known to us, or it
|
||||||
* it is a dynamic payload type for which we do not
|
* it is a dynamic payload type for which we do not
|
||||||
* know the audio name. We must give up here */
|
* know the audio name. We must give up here */
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR, "No audio codec name given, and payload type %d unknown\n",
|
||||||
|
payload_type);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,16 +179,23 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec, int payload_type,
|
|||||||
/* Now we extract the codec subtype name, rate and channels. The latter
|
/* Now we extract the codec subtype name, rate and channels. The latter
|
||||||
* two are optional. If they are not present we use the safe defaults
|
* two are optional. If they are not present we use the safe defaults
|
||||||
* above. */
|
* above. */
|
||||||
if (strlen(audio_name) > sizeof(audio_codec))
|
if (strlen(audio_name) > sizeof(audio_codec)) {
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR, "Audio codec too long: %s\n", osmo_quote_str(audio_name, -1));
|
||||||
goto error;
|
goto error;
|
||||||
|
}
|
||||||
channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
|
channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
|
||||||
rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
|
rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
|
||||||
if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1)
|
if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1) {
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR, "Invalid audio codec: %s\n", osmo_quote_str(audio_name, -1));
|
||||||
goto error;
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
/* Note: We only accept configurations with one audio channel! */
|
/* Note: We only accept configurations with one audio channel! */
|
||||||
if (channels != 1)
|
if (channels != 1) {
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR, "Cannot handle audio codec with more than one channel: %s\n",
|
||||||
|
osmo_quote_str(audio_name, -1));
|
||||||
goto error;
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
codec->rate = rate;
|
codec->rate = rate;
|
||||||
codec->channels = channels;
|
codec->channels = channels;
|
||||||
@@ -178,6 +213,7 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec, int payload_type,
|
|||||||
|
|
||||||
/* Derive the payload type if it is unknown */
|
/* Derive the payload type if it is unknown */
|
||||||
if (codec->payload_type == PTYPE_UNDEFINED) {
|
if (codec->payload_type == PTYPE_UNDEFINED) {
|
||||||
|
/* TODO: This is semi dead code, see OS#4150 */
|
||||||
|
|
||||||
/* For the known codecs from the static range we restore
|
/* For the known codecs from the static range we restore
|
||||||
* the IANA or 3GPP assigned payload type number */
|
* the IANA or 3GPP assigned payload type number */
|
||||||
@@ -213,9 +249,14 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec, int payload_type,
|
|||||||
* 110 onwards 3gpp defines prefered codec types, which are
|
* 110 onwards 3gpp defines prefered codec types, which are
|
||||||
* also fixed, see above) */
|
* also fixed, see above) */
|
||||||
if (codec->payload_type < 0) {
|
if (codec->payload_type < 0) {
|
||||||
|
/* FIXME: pt_offset is completely unrelated and useless here, any of those numbers may already
|
||||||
|
* have been added to the codecs. Instead, there should be an iterator checking for an actually
|
||||||
|
* unused dynamic payload type number. */
|
||||||
codec->payload_type = 96 + pt_offset;
|
codec->payload_type = 96 + pt_offset;
|
||||||
if (codec->payload_type > 109)
|
if (codec->payload_type > 109) {
|
||||||
|
LOGP(DLMGCP, LOGL_ERROR, "Ran out of payload type numbers to assign dynamically\n");
|
||||||
goto error;
|
goto error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,41 +267,14 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec, int payload_type,
|
|||||||
} else
|
} else
|
||||||
codec->param_present = false;
|
codec->param_present = false;
|
||||||
|
|
||||||
|
conn->end.codecs_assigned++;
|
||||||
return 0;
|
return 0;
|
||||||
error:
|
error:
|
||||||
/* Make sure we leave a clean codec entry on error. */
|
/* Make sure we leave a clean codec entry on error. */
|
||||||
codec_init(codec);
|
codec_free(codec);
|
||||||
memset(codec, 0, sizeof(*codec));
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Add codec configuration depending on payload type and/or codec name. This
|
|
||||||
* function uses the input parameters to extrapolate the full codec information.
|
|
||||||
* \param[out] codec configuration (caller provided memory).
|
|
||||||
* \param[out] conn related rtp-connection.
|
|
||||||
* \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
|
|
||||||
* \param[in] audio_name audio codec name, in uppercase (e.g. "GSM/8000/1").
|
|
||||||
* \param[in] param optional codec parameters (set to NULL when unused).
|
|
||||||
* \returns 0 on success, -EINVAL on failure. */
|
|
||||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
/* The amount of codecs we can store is limited, make sure we do not
|
|
||||||
* overrun this limit. */
|
|
||||||
if (conn->end.codecs_assigned >= MGCP_MAX_CODECS)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
rc = codec_set(conn->conn, &conn->end.codecs[conn->end.codecs_assigned], payload_type, audio_name,
|
|
||||||
conn->end.codecs_assigned, param);
|
|
||||||
if (rc != 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
conn->end.codecs_assigned++;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if the given codec is applicable on the specified endpoint
|
/* Check if the given codec is applicable on the specified endpoint
|
||||||
* Helper function for mgcp_codec_decide() */
|
* Helper function for mgcp_codec_decide() */
|
||||||
static bool is_codec_compatible(const struct mgcp_endpoint *endp, const struct mgcp_rtp_codec *codec)
|
static bool is_codec_compatible(const struct mgcp_endpoint *endp, const struct mgcp_rtp_codec *codec)
|
||||||
@@ -350,6 +364,25 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return true if octet-aligned is set in the given codec. Default to octet-aligned=0, i.e. bandwidth-efficient mode.
|
||||||
|
* See RFC4867 "RTP Payload Format for AMR and AMR-WB" sections "8.1. AMR Media Type Registration" and "8.2. AMR-WB
|
||||||
|
* Media Type Registration":
|
||||||
|
*
|
||||||
|
* octet-align: Permissible values are 0 and 1. If 1, octet-aligned
|
||||||
|
* operation SHALL be used. If 0 or if not present,
|
||||||
|
* bandwidth-efficient operation is employed.
|
||||||
|
*
|
||||||
|
* https://tools.ietf.org/html/rfc4867
|
||||||
|
*/
|
||||||
|
static bool amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
|
||||||
|
{
|
||||||
|
if (!codec->param_present)
|
||||||
|
return false;
|
||||||
|
if (!codec->param.amr_octet_aligned_present)
|
||||||
|
return false;
|
||||||
|
return codec->param.amr_octet_aligned;
|
||||||
|
}
|
||||||
|
|
||||||
/* Compare two codecs, all parameters must match up, except for the payload type
|
/* Compare two codecs, all parameters must match up, except for the payload type
|
||||||
* number. */
|
* number. */
|
||||||
static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b)
|
static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b)
|
||||||
@@ -362,10 +395,12 @@ static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *c
|
|||||||
return false;
|
return false;
|
||||||
if (codec_a->frame_duration_den != codec_b->frame_duration_den)
|
if (codec_a->frame_duration_den != codec_b->frame_duration_den)
|
||||||
return false;
|
return false;
|
||||||
if (strcmp(codec_a->audio_name, codec_b->audio_name))
|
|
||||||
return false;
|
|
||||||
if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
|
if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
|
||||||
return false;
|
return false;
|
||||||
|
if (!strcmp(codec_a->subtype_name, "AMR")) {
|
||||||
|
if (amr_is_octet_aligned(codec_a) != amr_is_octet_aligned(codec_b))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -416,3 +451,19 @@ int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp
|
|||||||
|
|
||||||
return codec_dst->payload_type;
|
return codec_dst->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)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < conn->end.codecs_assigned; i++) {
|
||||||
|
if (!strcmp(conn->end.codecs[i].subtype_name, subtype_name)) {
|
||||||
|
if (match_nr) {
|
||||||
|
match_nr--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return &conn->end.codecs[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
@@ -129,6 +129,7 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
|
|||||||
conn_osmux_disable(conn_rtp);
|
conn_osmux_disable(conn_rtp);
|
||||||
mgcp_free_rtp_port(&conn_rtp->end);
|
mgcp_free_rtp_port(&conn_rtp->end);
|
||||||
rate_ctr_group_free(conn_rtp->rate_ctr_group);
|
rate_ctr_group_free(conn_rtp->rate_ctr_group);
|
||||||
|
mgcp_codec_reset_all(conn_rtp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mgcp_conn_watchdog_cb(void *data)
|
void mgcp_conn_watchdog_cb(void *data)
|
||||||
|
@@ -45,6 +45,8 @@
|
|||||||
#include <osmocom/mgcp/mgcp_codec.h>
|
#include <osmocom/mgcp/mgcp_codec.h>
|
||||||
#include <osmocom/mgcp/debug.h>
|
#include <osmocom/mgcp/debug.h>
|
||||||
#include <osmocom/codec/codec.h>
|
#include <osmocom/codec/codec.h>
|
||||||
|
#include <osmocom/mgcp/iuup_cn_node.h>
|
||||||
|
#include <osmocom/mgcp/iuup_protocol.h>
|
||||||
|
|
||||||
|
|
||||||
#define RTP_SEQ_MOD (1 << 16)
|
#define RTP_SEQ_MOD (1 << 16)
|
||||||
@@ -52,11 +54,13 @@
|
|||||||
#define RTP_MAX_MISORDER 100
|
#define RTP_MAX_MISORDER 100
|
||||||
#define RTP_BUF_SIZE 4096
|
#define RTP_BUF_SIZE 4096
|
||||||
|
|
||||||
enum {
|
enum rtp_proto {
|
||||||
MGCP_PROTO_RTP,
|
MGCP_PROTO_RTP,
|
||||||
MGCP_PROTO_RTCP,
|
MGCP_PROTO_RTCP,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int rx_rtp(struct msgb *msg);
|
||||||
|
|
||||||
/*! Determine the local rtp bind IP-address.
|
/*! Determine the local rtp bind IP-address.
|
||||||
* \param[out] addr caller provided memory to store the resulting IP-Address
|
* \param[out] addr caller provided memory to store the resulting IP-Address
|
||||||
* \param[in] endp mgcp endpoint, that holds a copy of the VTY parameters
|
* \param[in] endp mgcp endpoint, that holds a copy of the VTY parameters
|
||||||
@@ -469,19 +473,43 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp,
|
|||||||
* Patch the payload type of an RTP packet so that it uses the payload type
|
* Patch the payload type of an RTP packet so that it uses the payload type
|
||||||
* that is valid for the destination connection (conn_dst) */
|
* that is valid for the destination connection (conn_dst) */
|
||||||
static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_src,
|
static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_src,
|
||||||
struct mgcp_conn_rtp *conn_dst, char *data, int len)
|
struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
|
||||||
{
|
{
|
||||||
struct rtp_hdr *rtp_hdr;
|
struct rtp_hdr *rtp_hdr;
|
||||||
uint8_t pt_in;
|
uint8_t pt_in;
|
||||||
int pt_out;
|
int pt_out;
|
||||||
|
|
||||||
OSMO_ASSERT(len >= sizeof(struct rtp_hdr));
|
if (msg->len < sizeof(struct rtp_hdr)) {
|
||||||
rtp_hdr = (struct rtp_hdr *)data;
|
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTP packet too short (%u < %zu)\n",
|
||||||
|
msg->len, sizeof(struct rtp_hdr));
|
||||||
pt_in = rtp_hdr->payload_type;
|
|
||||||
pt_out = mgcp_codec_pt_translate(conn_src, conn_dst, pt_in);
|
|
||||||
if (pt_out < 0)
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtp_hdr = (struct rtp_hdr *)msg->data;
|
||||||
|
|
||||||
|
if (conn_src->iuup) {
|
||||||
|
/* The source is an IuUP payload. We have received a dynamic payload type number on the IuUP side, and
|
||||||
|
* towards the pure RTP side it should go out as "AMR/8000". Make sure that the payload type number in
|
||||||
|
* the RTP packet matches the a=rtpmap:N payload type number configured for AMR. */
|
||||||
|
const struct mgcp_rtp_codec *amr_codec = mgcp_codec_pt_find_by_subtype_name(conn_dst, "AMR", 0);
|
||||||
|
|
||||||
|
if (!amr_codec) {
|
||||||
|
/* There is no AMR codec configured on the outgoing conn. */
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pt_out = amr_codec->payload_type;
|
||||||
|
} else if (conn_dst->iuup) {
|
||||||
|
/* The destination is an IuUP payload. Use whatever payload number was negotiated during IuUP
|
||||||
|
* Initialization. */
|
||||||
|
pt_out = conn_dst->iuup->rtp_payload_type;
|
||||||
|
} else {
|
||||||
|
/* Both sides are normal RTP payloads. Consult the rtpmap settings received by SDP. */
|
||||||
|
pt_in = rtp_hdr->payload_type;
|
||||||
|
pt_out = mgcp_codec_pt_translate(conn_src, conn_dst, pt_in);
|
||||||
|
if (pt_out < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
rtp_hdr->payload_type = (uint8_t) pt_out;
|
rtp_hdr->payload_type = (uint8_t) pt_out;
|
||||||
return 0;
|
return 0;
|
||||||
@@ -496,7 +524,7 @@ static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_src,
|
|||||||
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||||
struct mgcp_rtp_state *state,
|
struct mgcp_rtp_state *state,
|
||||||
struct mgcp_rtp_end *rtp_end,
|
struct mgcp_rtp_end *rtp_end,
|
||||||
struct sockaddr_in *addr, char *data, int len)
|
struct sockaddr_in *addr, struct msgb *msg)
|
||||||
{
|
{
|
||||||
uint32_t arrival_time;
|
uint32_t arrival_time;
|
||||||
int32_t transit;
|
int32_t transit;
|
||||||
@@ -504,11 +532,12 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
|||||||
uint32_t timestamp, ssrc;
|
uint32_t timestamp, ssrc;
|
||||||
struct rtp_hdr *rtp_hdr;
|
struct rtp_hdr *rtp_hdr;
|
||||||
int payload = rtp_end->codec->payload_type;
|
int payload = rtp_end->codec->payload_type;
|
||||||
|
unsigned int len = msg->len;
|
||||||
|
|
||||||
if (len < sizeof(*rtp_hdr))
|
if (len < sizeof(*rtp_hdr))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
rtp_hdr = (struct rtp_hdr *)data;
|
rtp_hdr = (struct rtp_hdr *)msg->data;
|
||||||
seq = ntohs(rtp_hdr->sequence);
|
seq = ntohs(rtp_hdr->sequence);
|
||||||
timestamp = ntohl(rtp_hdr->timestamp);
|
timestamp = ntohl(rtp_hdr->timestamp);
|
||||||
arrival_time = get_current_ts(rtp_end->codec->rate);
|
arrival_time = get_current_ts(rtp_end->codec->rate);
|
||||||
@@ -641,7 +670,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
|||||||
* the receiving end expects GSM-HR data to be formated after RFC 5993, this
|
* the receiving end expects GSM-HR data to be formated after RFC 5993, this
|
||||||
* function is used to convert between RFC 5993 and TS 101318, which we normally
|
* function is used to convert between RFC 5993 and TS 101318, which we normally
|
||||||
* use. */
|
* use. */
|
||||||
static void rfc5993_hr_convert(struct mgcp_endpoint *endp, char *data, int *len)
|
static int rfc5993_hr_convert(struct mgcp_endpoint *endp, char *data, int *len)
|
||||||
{
|
{
|
||||||
/* NOTE: *data has an overall length of RTP_BUF_SIZE, so there is
|
/* NOTE: *data has an overall length of RTP_BUF_SIZE, so there is
|
||||||
* plenty of space available to store the slightly larger, converted
|
* plenty of space available to store the slightly larger, converted
|
||||||
@@ -649,7 +678,12 @@ static void rfc5993_hr_convert(struct mgcp_endpoint *endp, char *data, int *len)
|
|||||||
|
|
||||||
struct rtp_hdr *rtp_hdr;
|
struct rtp_hdr *rtp_hdr;
|
||||||
|
|
||||||
OSMO_ASSERT(*len >= sizeof(struct rtp_hdr));
|
if (*len < sizeof(struct rtp_hdr)) {
|
||||||
|
LOGPENDP(endp, DRTP, LOGL_ERROR, "AMR RTP packet too short (%d < %zu)\n",
|
||||||
|
*len, sizeof(struct rtp_hdr));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
rtp_hdr = (struct rtp_hdr *)data;
|
rtp_hdr = (struct rtp_hdr *)data;
|
||||||
|
|
||||||
if (*len == GSM_HR_BYTES + sizeof(struct rtp_hdr)) {
|
if (*len == GSM_HR_BYTES + sizeof(struct rtp_hdr)) {
|
||||||
@@ -667,7 +701,9 @@ static void rfc5993_hr_convert(struct mgcp_endpoint *endp, char *data, int *len)
|
|||||||
* packet. This is not supported yet. */
|
* packet. This is not supported yet. */
|
||||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||||
"cannot figure out how to convert RTP packet\n");
|
"cannot figure out how to convert RTP packet\n");
|
||||||
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For AMR RTP two framing modes are defined RFC3267. There is a bandwith
|
/* For AMR RTP two framing modes are defined RFC3267. There is a bandwith
|
||||||
@@ -685,7 +721,11 @@ static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, char *data, int *len,
|
|||||||
unsigned int payload_len;
|
unsigned int payload_len;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
OSMO_ASSERT(*len >= sizeof(struct rtp_hdr));
|
if (*len < sizeof(struct rtp_hdr)) {
|
||||||
|
LOGPENDP(endp, DRTP, LOGL_ERROR, "AMR RTP packet too short (%d < %zu)\n", *len, sizeof(struct rtp_hdr));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
rtp_hdr = (struct rtp_hdr *)data;
|
rtp_hdr = (struct rtp_hdr *)data;
|
||||||
|
|
||||||
payload_len = *len - sizeof(struct rtp_hdr);
|
payload_len = *len - sizeof(struct rtp_hdr);
|
||||||
@@ -736,30 +776,33 @@ static bool amr_oa_bwe_convert_indicated(struct mgcp_rtp_codec *codec)
|
|||||||
|
|
||||||
|
|
||||||
/* Check if a given RTP with AMR payload for octet-aligned mode */
|
/* Check if a given RTP with AMR payload for octet-aligned mode */
|
||||||
static bool amr_oa_check(char *data, int len)
|
static int amr_oa_check(char *data, int len)
|
||||||
{
|
{
|
||||||
struct rtp_hdr *rtp_hdr;
|
struct rtp_hdr *rtp_hdr;
|
||||||
unsigned int payload_len;
|
unsigned int payload_len;
|
||||||
|
|
||||||
OSMO_ASSERT(len >= sizeof(struct rtp_hdr));
|
if (len < sizeof(struct rtp_hdr))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
rtp_hdr = (struct rtp_hdr *)data;
|
rtp_hdr = (struct rtp_hdr *)data;
|
||||||
|
|
||||||
payload_len = len - sizeof(struct rtp_hdr);
|
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);
|
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
|
/* Forward data to a debug tap. This is debug function that is intended for
|
||||||
* debugging the voice traffic with tools like gstreamer */
|
* debugging the voice traffic with tools like gstreamer */
|
||||||
static void forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf,
|
static void forward_data(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg)
|
||||||
int len)
|
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (!tap->enabled)
|
if (!tap->enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
rc = sendto(fd, buf, len, 0, (struct sockaddr *)&tap->forward,
|
rc = sendto(fd, msg->data, msg->len, 0, (struct sockaddr *)&tap->forward,
|
||||||
sizeof(tap->forward));
|
sizeof(tap->forward));
|
||||||
|
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
@@ -777,7 +820,7 @@ static void forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf,
|
|||||||
* \param[in] conn_dst associated destination connection
|
* \param[in] conn_dst associated destination connection
|
||||||
* \returns 0 on success, -1 on ERROR */
|
* \returns 0 on success, -1 on ERROR */
|
||||||
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||||
char *buf, int len, struct mgcp_conn_rtp *conn_src,
|
struct msgb *msg, struct mgcp_conn_rtp *conn_src,
|
||||||
struct mgcp_conn_rtp *conn_dst)
|
struct mgcp_conn_rtp *conn_dst)
|
||||||
{
|
{
|
||||||
/*! When no destination connection is available (e.g. when only one
|
/*! When no destination connection is available (e.g. when only one
|
||||||
@@ -789,6 +832,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
|||||||
struct mgcp_rtp_state *rtp_state;
|
struct mgcp_rtp_state *rtp_state;
|
||||||
char *dest_name;
|
char *dest_name;
|
||||||
int rc;
|
int rc;
|
||||||
|
int len;
|
||||||
|
|
||||||
OSMO_ASSERT(conn_src);
|
OSMO_ASSERT(conn_src);
|
||||||
OSMO_ASSERT(conn_dst);
|
OSMO_ASSERT(conn_dst);
|
||||||
@@ -812,7 +856,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
|||||||
* should not occur if transcoding is consequently avoided. Until
|
* should not occur if transcoding is consequently avoided. Until
|
||||||
* we have transcoding support in osmo-mgw we can not resolve this. */
|
* we have transcoding support in osmo-mgw we can not resolve this. */
|
||||||
if (is_rtp) {
|
if (is_rtp) {
|
||||||
rc = mgcp_patch_pt(conn_src, conn_dst, buf, len);
|
rc = mgcp_patch_pt(conn_src, conn_dst, msg);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||||
"can not patch PT because no suitable egress codec was found.\n");
|
"can not patch PT because no suitable egress codec was found.\n");
|
||||||
@@ -837,27 +881,37 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
|||||||
} else if (is_rtp) {
|
} else if (is_rtp) {
|
||||||
int cont;
|
int cont;
|
||||||
int nbytes = 0;
|
int nbytes = 0;
|
||||||
int buflen = len;
|
int buflen = msg->len;
|
||||||
do {
|
do {
|
||||||
/* Run transcoder */
|
/* Run transcoder */
|
||||||
cont = endp->cfg->rtp_processing_cb(endp, rtp_end,
|
cont = endp->cfg->rtp_processing_cb(endp, rtp_end,
|
||||||
buf, &buflen,
|
(char*)msg->data, &buflen,
|
||||||
RTP_BUF_SIZE);
|
RTP_BUF_SIZE);
|
||||||
if (cont < 0)
|
if (cont < 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (addr)
|
if (addr)
|
||||||
mgcp_patch_and_count(endp, rtp_state, rtp_end,
|
mgcp_patch_and_count(endp, rtp_state, rtp_end,
|
||||||
addr, buf, buflen);
|
addr, msg);
|
||||||
|
|
||||||
if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) {
|
if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) {
|
||||||
amr_oa_bwe_convert(endp, buf, &buflen,
|
rc = amr_oa_bwe_convert(endp, (char*)msg->data, &buflen,
|
||||||
conn_dst->end.codec->param.amr_octet_aligned);
|
conn_dst->end.codec->param.amr_octet_aligned);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||||
|
"Error in AMR octet-aligned <-> bandwidth-efficient mode conversion\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (rtp_end->rfc5993_hr_convert
|
else if (rtp_end->rfc5993_hr_convert
|
||||||
&& strcmp(conn_src->end.codec->subtype_name,
|
&& strcmp(conn_src->end.codec->subtype_name,
|
||||||
"GSM-HR-08") == 0)
|
"GSM-HR-08") == 0) {
|
||||||
rfc5993_hr_convert(endp, buf, &buflen);
|
rc = rfc5993_hr_convert(endp, (char*)msg->data, &buflen);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGPENDP(endp, DRTP, LOGL_ERROR, "Error while converting to GSM-HR-08\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||||
"process/send to %s %s "
|
"process/send to %s %s "
|
||||||
@@ -868,8 +922,9 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
|||||||
|
|
||||||
/* Forward a copy of the RTP data to a debug ip/port */
|
/* Forward a copy of the RTP data to a debug ip/port */
|
||||||
forward_data(rtp_end->rtp.fd, &conn_src->tap_out,
|
forward_data(rtp_end->rtp.fd, &conn_src->tap_out,
|
||||||
buf, buflen);
|
msg);
|
||||||
|
|
||||||
|
#if 0
|
||||||
/* FIXME: HACK HACK HACK. See OS#2459.
|
/* FIXME: HACK HACK HACK. See OS#2459.
|
||||||
* The ip.access nano3G needs the first RTP payload's first two bytes to read hex
|
* 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
|
* 'e400', or it will reject the RAB assignment. It seems to not harm other femto
|
||||||
@@ -877,7 +932,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
|||||||
*/
|
*/
|
||||||
if (!rtp_state->patched_first_rtp_payload
|
if (!rtp_state->patched_first_rtp_payload
|
||||||
&& conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
|
&& conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
|
||||||
uint8_t *data = (uint8_t *) & buf[12];
|
uint8_t *data = msg->data + 12;
|
||||||
if (data[0] == 0xe0) {
|
if (data[0] == 0xe0) {
|
||||||
data[0] = 0xe4;
|
data[0] = 0xe4;
|
||||||
data[1] = 0x00;
|
data[1] = 0x00;
|
||||||
@@ -887,10 +942,13 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
|||||||
" to fake an IuUP Initialization Ack\n");
|
" to fake an IuUP Initialization Ack\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
len = mgcp_udp_send(rtp_end->rtp.fd,
|
if (conn_dst->iuup)
|
||||||
&rtp_end->addr,
|
len = osmo_iuup_cn_tx_payload(conn_dst->iuup, msg);
|
||||||
rtp_end->rtp_port, buf, buflen);
|
else
|
||||||
|
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port,
|
||||||
|
(char*)msg->data, msg->len);
|
||||||
|
|
||||||
if (len <= 0)
|
if (len <= 0)
|
||||||
return len;
|
return len;
|
||||||
@@ -911,7 +969,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
|||||||
|
|
||||||
len = mgcp_udp_send(rtp_end->rtcp.fd,
|
len = mgcp_udp_send(rtp_end->rtcp.fd,
|
||||||
&rtp_end->addr,
|
&rtp_end->addr,
|
||||||
rtp_end->rtcp_port, buf, len);
|
rtp_end->rtcp_port, (char*)msg->data, msg->len);
|
||||||
|
|
||||||
rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
|
rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
|
||||||
rate_ctr_add(&conn_dst->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], len);
|
rate_ctr_add(&conn_dst->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], len);
|
||||||
@@ -922,45 +980,6 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Helper function for mgcp_recv(),
|
|
||||||
Receive one RTP Packet + Originating address from file descriptor */
|
|
||||||
static int receive_from(struct mgcp_endpoint *endp, int fd,
|
|
||||||
struct sockaddr_in *addr, char *buf, int bufsize)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
socklen_t slen = sizeof(*addr);
|
|
||||||
struct sockaddr_in addr_sink;
|
|
||||||
char buf_sink[RTP_BUF_SIZE];
|
|
||||||
bool tossed = false;
|
|
||||||
|
|
||||||
if (!addr)
|
|
||||||
addr = &addr_sink;
|
|
||||||
if (!buf) {
|
|
||||||
tossed = true;
|
|
||||||
buf = buf_sink;
|
|
||||||
bufsize = sizeof(buf_sink);
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = recvfrom(fd, buf, bufsize, 0, (struct sockaddr *)addr, &slen);
|
|
||||||
|
|
||||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
|
||||||
"receiving %u bytes length packet from %s:%u ...\n",
|
|
||||||
rc, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
|
||||||
|
|
||||||
if (rc < 0) {
|
|
||||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
|
||||||
"failed to receive packet, errno: %d/%s\n",
|
|
||||||
errno, strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tossed) {
|
|
||||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "packet tossed\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if the origin (addr) matches the address/port data of the RTP
|
/* Check if the origin (addr) matches the address/port data of the RTP
|
||||||
* connections. */
|
* connections. */
|
||||||
static int check_rtp_origin(struct mgcp_conn_rtp *conn,
|
static int check_rtp_origin(struct mgcp_conn_rtp *conn,
|
||||||
@@ -1053,7 +1072,7 @@ static int check_rtp_destin(struct mgcp_conn_rtp *conn)
|
|||||||
|
|
||||||
/* Do some basic checks to make sure that the RTCP packets we are going to
|
/* Do some basic checks to make sure that the RTCP packets we are going to
|
||||||
* process are not complete garbage */
|
* process are not complete garbage */
|
||||||
static int check_rtcp(char *buf, unsigned int buf_size)
|
static int check_rtcp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
|
||||||
{
|
{
|
||||||
struct rtcp_hdr *hdr;
|
struct rtcp_hdr *hdr;
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
@@ -1061,33 +1080,45 @@ static int check_rtcp(char *buf, unsigned int buf_size)
|
|||||||
|
|
||||||
/* RTPC packets that are just a header without data do not make
|
/* RTPC packets that are just a header without data do not make
|
||||||
* any sense. */
|
* any sense. */
|
||||||
if (buf_size < sizeof(struct rtcp_hdr))
|
if (msg->len < sizeof(struct rtcp_hdr)) {
|
||||||
|
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP packet too short (%u < %zu)\n",
|
||||||
|
msg->len, sizeof(struct rtcp_hdr));
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Make sure that the length of the received packet does not exceed
|
/* Make sure that the length of the received packet does not exceed
|
||||||
* the available buffer size */
|
* the available buffer size */
|
||||||
hdr = (struct rtcp_hdr *)buf;
|
hdr = (struct rtcp_hdr *)msg->data;
|
||||||
len = (osmo_ntohs(hdr->length) + 1) * 4;
|
len = (osmo_ntohs(hdr->length) + 1) * 4;
|
||||||
if (len > buf_size)
|
if (len > msg->len) {
|
||||||
|
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP header length exceeds packet size (%u > %u)\n",
|
||||||
|
len, msg->len);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Make sure we accept only packets that have a proper packet type set
|
/* Make sure we accept only packets that have a proper packet type set
|
||||||
* See also: http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
|
* See also: http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
|
||||||
type = hdr->type;
|
type = hdr->type;
|
||||||
if ((type < 192 || type > 195) && (type < 200 || type > 213))
|
if ((type < 192 || type > 195) && (type < 200 || type > 213)) {
|
||||||
|
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP header: invalid type: %u\n", type);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do some basic checks to make sure that the RTP packets we are going to
|
/* Do some basic checks to make sure that the RTP packets we are going to
|
||||||
* process are not complete garbage */
|
* process are not complete garbage */
|
||||||
static int check_rtp(char *buf, unsigned int buf_size)
|
static int check_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
|
||||||
{
|
{
|
||||||
/* RTP packets that are just a header without data do not make
|
size_t min_size = sizeof(struct rtp_hdr);
|
||||||
* any sense. */
|
if (conn_src->iuup)
|
||||||
if (buf_size < sizeof(struct rtp_hdr))
|
min_size += sizeof(struct osmo_iuup_hdr_data);
|
||||||
return -EINVAL;
|
if (msg->len < min_size) {
|
||||||
|
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTP packet too short (%u < %zu)\n",
|
||||||
|
msg->len, min_size);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* FIXME: Add more checks, the reason why we do not check more than
|
/* FIXME: Add more checks, the reason why we do not check more than
|
||||||
* the length is because we currently handle IUUP packets as RTP
|
* the length is because we currently handle IUUP packets as RTP
|
||||||
@@ -1098,86 +1129,14 @@ static int check_rtp(char *buf, unsigned int buf_size)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Receive RTP data from a specified source connection and dispatch it to a
|
|
||||||
* destination connection. */
|
|
||||||
static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
|
|
||||||
unsigned int buf_size, struct osmo_fd *fd)
|
|
||||||
{
|
|
||||||
struct mgcp_endpoint *endp;
|
|
||||||
struct mgcp_conn_rtp *conn;
|
|
||||||
struct mgcp_trunk_config *tcfg;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
conn = (struct mgcp_conn_rtp*) fd->data;
|
|
||||||
endp = conn->conn->endp;
|
|
||||||
tcfg = endp->tcfg;
|
|
||||||
|
|
||||||
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG, "receiving RTP/RTCP packet...\n");
|
|
||||||
|
|
||||||
rc = receive_from(endp, fd->fd, addr, buf, buf_size);
|
|
||||||
if (rc <= 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* FIXME: The way how we detect the protocol looks odd. We should look
|
|
||||||
* into the packet header. Also we should introduce a packet type
|
|
||||||
* MGCP_PROTO_IUUP because currently we handle IUUP packets like RTP
|
|
||||||
* packets which is problematic. */
|
|
||||||
*proto = fd == &conn->end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
|
|
||||||
|
|
||||||
if (*proto == MGCP_PROTO_RTP) {
|
|
||||||
if (check_rtp(buf, rc) < 0) {
|
|
||||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
|
||||||
"invalid RTP packet received -- packet tossed\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else if (*proto == MGCP_PROTO_RTCP) {
|
|
||||||
if (check_rtcp(buf, rc) < 0) {
|
|
||||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
|
||||||
"invalid RTCP packet received -- packet tossed\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG, "");
|
|
||||||
LOGPC(DRTP, LOGL_DEBUG, "receiving from %s %s %d\n",
|
|
||||||
conn->conn->name, inet_ntoa(addr->sin_addr),
|
|
||||||
ntohs(addr->sin_port));
|
|
||||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn->conn));
|
|
||||||
|
|
||||||
/* Check if the origin of the RTP packet seems plausible */
|
|
||||||
if (tcfg->rtp_accept_all == 0) {
|
|
||||||
if (check_rtp_origin(conn, addr) != 0)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Filter out dummy message */
|
|
||||||
if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) {
|
|
||||||
LOGPCONN(conn->conn, DRTP, LOGL_NOTICE,
|
|
||||||
"dummy message received\n");
|
|
||||||
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
|
|
||||||
"packet tossed\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Increment RX statistics */
|
|
||||||
rate_ctr_inc(&conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]);
|
|
||||||
rate_ctr_add(&conn->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR], rc);
|
|
||||||
|
|
||||||
/* Forward a copy of the RTP data to a debug ip/port */
|
|
||||||
forward_data(fd->fd, &conn->tap_in, buf, rc);
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send RTP data. Possible options are standard RTP packet
|
/* Send RTP data. Possible options are standard RTP packet
|
||||||
* transmission or trsmission via an osmux connection */
|
* transmission or trsmission via an osmux connection */
|
||||||
static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
|
static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
|
||||||
unsigned int buf_size,
|
|
||||||
struct mgcp_conn_rtp *conn_src,
|
|
||||||
struct mgcp_conn_rtp *conn_dst)
|
|
||||||
{
|
{
|
||||||
struct mgcp_endpoint *endp;
|
enum rtp_proto proto = OSMO_RTP_MSG_CTX(msg)->proto;
|
||||||
endp = conn_src->conn->endp;
|
struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
|
||||||
|
struct sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
|
||||||
|
struct mgcp_endpoint *endp = conn_src->conn->endp;
|
||||||
|
|
||||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "destin conn:%s\n",
|
LOGPENDP(endp, DRTP, LOGL_DEBUG, "destin conn:%s\n",
|
||||||
mgcp_conn_dump(conn_dst->conn));
|
mgcp_conn_dump(conn_dst->conn));
|
||||||
@@ -1196,13 +1155,13 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
|
|||||||
"endpoint type is MGCP_RTP_DEFAULT, "
|
"endpoint type is MGCP_RTP_DEFAULT, "
|
||||||
"using mgcp_send() to forward data directly\n");
|
"using mgcp_send() to forward data directly\n");
|
||||||
return mgcp_send(endp, proto == MGCP_PROTO_RTP,
|
return mgcp_send(endp, proto == MGCP_PROTO_RTP,
|
||||||
addr, buf, buf_size, conn_src, conn_dst);
|
from_addr, msg, conn_src, conn_dst);
|
||||||
case MGCP_OSMUX_BSC_NAT:
|
case MGCP_OSMUX_BSC_NAT:
|
||||||
case MGCP_OSMUX_BSC:
|
case MGCP_OSMUX_BSC:
|
||||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||||
"endpoint type is MGCP_OSMUX_BSC_NAT, "
|
"endpoint type is MGCP_OSMUX_BSC_NAT, "
|
||||||
"using osmux_xfrm_to_osmux() to forward data through OSMUX\n");
|
"using osmux_xfrm_to_osmux() to forward data through OSMUX\n");
|
||||||
return osmux_xfrm_to_osmux(buf, buf_size, conn_dst);
|
return osmux_xfrm_to_osmux((char*)msg->data, msg->len, conn_dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the data has not been handled/forwarded until here, it will
|
/* If the data has not been handled/forwarded until here, it will
|
||||||
@@ -1220,10 +1179,12 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
|
|||||||
* \param[in] buf_size size data length of buf
|
* \param[in] buf_size size data length of buf
|
||||||
* \param[in] conn originating connection
|
* \param[in] conn originating connection
|
||||||
* \returns 0 on success, -1 on ERROR */
|
* \returns 0 on success, -1 on ERROR */
|
||||||
int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
|
int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
|
||||||
unsigned int buf_size, struct mgcp_conn *conn)
|
|
||||||
{
|
{
|
||||||
|
struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
|
||||||
|
struct mgcp_conn *conn = conn_src->conn;
|
||||||
struct mgcp_conn *conn_dst;
|
struct mgcp_conn *conn_dst;
|
||||||
|
struct sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
|
||||||
|
|
||||||
/*! NOTE: This callback function implements the endpoint specific
|
/*! NOTE: This callback function implements the endpoint specific
|
||||||
* dispatch bahviour of an rtp bridge/proxy endpoint. It is assumed
|
* dispatch bahviour of an rtp bridge/proxy endpoint. It is assumed
|
||||||
@@ -1242,11 +1203,10 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
|
|||||||
* address data from the UDP packet header to patch the
|
* address data from the UDP packet header to patch the
|
||||||
* outgoing address in connection on the fly */
|
* outgoing address in connection on the fly */
|
||||||
if (conn->u.rtp.end.rtp_port == 0) {
|
if (conn->u.rtp.end.rtp_port == 0) {
|
||||||
conn->u.rtp.end.addr = addr->sin_addr;
|
conn->u.rtp.end.addr = from_addr->sin_addr;
|
||||||
conn->u.rtp.end.rtp_port = addr->sin_port;
|
conn->u.rtp.end.rtp_port = from_addr->sin_port;
|
||||||
}
|
}
|
||||||
return mgcp_send_rtp(proto, addr, buf,
|
return mgcp_send_rtp(conn_src, msg);
|
||||||
buf_size, &conn->u.rtp, &conn->u.rtp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find a destination connection. */
|
/* Find a destination connection. */
|
||||||
@@ -1278,9 +1238,7 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Dispatch RTP packet to destination RTP connection */
|
/* Dispatch RTP packet to destination RTP connection */
|
||||||
return mgcp_send_rtp(proto, addr, buf,
|
return mgcp_send_rtp(&conn_dst->u.rtp, msg);
|
||||||
buf_size, &conn->u.rtp, &conn_dst->u.rtp);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! cleanup an endpoint when a connection on an RTP bridge endpoint is removed.
|
/*! cleanup an endpoint when a connection on an RTP bridge endpoint is removed.
|
||||||
@@ -1302,6 +1260,76 @@ void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_dummy_msg(enum rtp_proto proto, struct msgb *msg)
|
||||||
|
{
|
||||||
|
return msg->len == 1 && msg->data[0] == MGCP_DUMMY_LOAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pdu_ctx {
|
||||||
|
struct sockaddr_in *from_addr;
|
||||||
|
struct mgcp_conn_rtp *conn_src;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* IuUP CN node has stripped an IuUP header and forwards RTP data to distribute to the peers. */
|
||||||
|
int iuup_rx_payload(struct msgb *msg, void *node_priv)
|
||||||
|
{
|
||||||
|
struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
|
||||||
|
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "iuup_rx_payload(%u bytes)\n", msg->len);
|
||||||
|
return rx_rtp(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IuUP CN node has composed a message that contains an IuUP header and asks us to send to the IuUP peer.
|
||||||
|
*/
|
||||||
|
int iuup_tx_msg(struct msgb *msg, void *node_priv)
|
||||||
|
{
|
||||||
|
const struct in_addr zero_addr = {};
|
||||||
|
struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
|
||||||
|
struct mgcp_conn_rtp *conn_dst = node_priv;
|
||||||
|
struct sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
|
||||||
|
struct mgcp_rtp_end *rtp_end = &conn_dst->end;
|
||||||
|
struct in_addr to_addr = rtp_end->addr;
|
||||||
|
uint16_t to_port = rtp_end->rtp_port;
|
||||||
|
|
||||||
|
if (conn_src == conn_dst
|
||||||
|
&& !memcmp(&zero_addr, &to_addr, sizeof(zero_addr)) && !to_port) {
|
||||||
|
LOG_CONN_RTP(conn_dst, LOGL_DEBUG, "iuup_tx_msg(): direct IuUP reply\n");
|
||||||
|
/* IuUP wants to send a message back to the same peer that sent an RTP package, but there
|
||||||
|
* is no address configured for that peer yet. It is probably an IuUP Initialization ACK
|
||||||
|
* reply. Use the sender address to send the reply.
|
||||||
|
*
|
||||||
|
* During 3G RAB Assignment, a 3G cell might first probe the MGW and expect an IuUP
|
||||||
|
* Initialization ACK before it replies to the MSC with a successful RAB Assignment; only
|
||||||
|
* after that reply does MSC officially know which RTP address+port the 3G cell wants to
|
||||||
|
* use and can tell this MGW about it, so this "loopback" is, for some 3G cells, the only
|
||||||
|
* chance we have to get a successful RAB Assignment done (particularly the nano3G does
|
||||||
|
* this). */
|
||||||
|
to_addr = from_addr->sin_addr;
|
||||||
|
to_port = from_addr->sin_port;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_CONN_RTP(conn_dst, LOGL_DEBUG, "iuup_tx_msg(%u bytes) to %s:%u\n", msg->len,
|
||||||
|
inet_ntoa(to_addr), ntohs(to_port));
|
||||||
|
|
||||||
|
return mgcp_udp_send(rtp_end->rtp.fd, &to_addr, to_port, (char*)msg->data, msg->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void iuup_init(struct mgcp_conn_rtp *conn_src)
|
||||||
|
{
|
||||||
|
struct osmo_iuup_cn_cfg cfg = {
|
||||||
|
.node_priv = conn_src,
|
||||||
|
.rx_payload = iuup_rx_payload,
|
||||||
|
.tx_msg = iuup_tx_msg,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (conn_src->iuup) {
|
||||||
|
LOG_CONN_RTP(conn_src, LOGL_NOTICE, "Rx IuUP init, but already initialized. Ignoring.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
conn_src->iuup = osmo_iuup_cn_init(conn_src->conn, &cfg, "endp_%d_conn_%s",
|
||||||
|
ENDPOINT_NUMBER(conn_src->conn->endp), conn_src->conn->id);
|
||||||
|
}
|
||||||
|
|
||||||
/* Handle incoming RTP data from NET */
|
/* Handle incoming RTP data from NET */
|
||||||
static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||||
{
|
{
|
||||||
@@ -1315,23 +1343,88 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
|||||||
struct mgcp_conn_rtp *conn_src;
|
struct mgcp_conn_rtp *conn_src;
|
||||||
struct mgcp_endpoint *endp;
|
struct mgcp_endpoint *endp;
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
|
socklen_t slen = sizeof(addr);
|
||||||
char buf[RTP_BUF_SIZE];
|
int ret;
|
||||||
int proto;
|
enum rtp_proto proto;
|
||||||
int len;
|
struct osmo_rtp_msg_ctx mc;
|
||||||
|
struct msgb *msg = msgb_alloc_headroom(RTP_BUF_SIZE + OSMO_IUUP_HEADROOM,
|
||||||
|
OSMO_IUUP_HEADROOM, "RTP-rx");
|
||||||
|
int rc;
|
||||||
|
|
||||||
conn_src = (struct mgcp_conn_rtp *)fd->data;
|
conn_src = (struct mgcp_conn_rtp *)fd->data;
|
||||||
OSMO_ASSERT(conn_src);
|
OSMO_ASSERT(conn_src);
|
||||||
endp = conn_src->conn->endp;
|
endp = conn_src->conn->endp;
|
||||||
OSMO_ASSERT(endp);
|
OSMO_ASSERT(endp);
|
||||||
|
|
||||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "source conn:%s\n",
|
proto = (fd == &conn_src->end.rtp)? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
|
||||||
mgcp_conn_dump(conn_src->conn));
|
|
||||||
|
|
||||||
/* Receive packet */
|
ret = recvfrom(fd->fd, msg->data, msg->data_len, 0, (struct sockaddr *)&addr, &slen);
|
||||||
len = mgcp_recv(&proto, &addr, buf, sizeof(buf), fd);
|
|
||||||
if (len < 0)
|
if (ret <= 0) {
|
||||||
return -1;
|
LOG_CONN_RTP(conn_src, LOGL_ERROR, "recvfrom error: %s\n", strerror(errno));
|
||||||
|
rc = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
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",
|
||||||
|
msg->len, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
|
||||||
|
|
||||||
|
if ((proto == MGCP_PROTO_RTP && check_rtp(conn_src, msg))
|
||||||
|
|| (proto == MGCP_PROTO_RTCP && check_rtcp(conn_src, msg))) {
|
||||||
|
/* Logging happened in the two check_ functions */
|
||||||
|
rc = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_dummy_msg(proto, msg)) {
|
||||||
|
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx dummy packet (dropped)\n");
|
||||||
|
rc = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
mc = (struct osmo_rtp_msg_ctx){
|
||||||
|
.proto = proto,
|
||||||
|
.conn_src = conn_src,
|
||||||
|
.from_addr = &addr,
|
||||||
|
};
|
||||||
|
OSMO_RTP_MSG_CTX(msg) = &mc;
|
||||||
|
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "msg ctx: %d %p %s\n",
|
||||||
|
OSMO_RTP_MSG_CTX(msg)->proto,
|
||||||
|
OSMO_RTP_MSG_CTX(msg)->conn_src,
|
||||||
|
osmo_hexdump((void*)OSMO_RTP_MSG_CTX(msg)->from_addr, sizeof(struct sockaddr_in)));
|
||||||
|
|
||||||
|
/* Increment RX statistics */
|
||||||
|
rate_ctr_inc(&conn_src->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]);
|
||||||
|
rate_ctr_add(&conn_src->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR], msg->len);
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
if (proto == MGCP_PROTO_RTP && osmo_iuup_is_init(msg))
|
||||||
|
iuup_init(conn_src);
|
||||||
|
|
||||||
|
if (conn_src->iuup && proto == MGCP_PROTO_RTP)
|
||||||
|
rc = osmo_iuup_cn_rx_pdu(conn_src->iuup, msg);
|
||||||
|
else
|
||||||
|
rc = rx_rtp(msg);
|
||||||
|
|
||||||
|
out:
|
||||||
|
msgb_free(msg);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rx_rtp(struct msgb *msg)
|
||||||
|
{
|
||||||
|
struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
|
||||||
|
struct sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
|
||||||
|
struct mgcp_conn *conn = conn_src->conn;
|
||||||
|
struct mgcp_trunk_config *tcfg = conn->endp->tcfg;
|
||||||
|
|
||||||
|
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx_rtp(%u bytes)\n", msg->len);
|
||||||
|
|
||||||
mgcp_conn_watchdog_kick(conn_src->conn);
|
mgcp_conn_watchdog_kick(conn_src->conn);
|
||||||
|
|
||||||
@@ -1340,14 +1433,20 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
|||||||
* define, then we check if the incoming payload matches that
|
* define, then we check if the incoming payload matches that
|
||||||
* expectation. */
|
* expectation. */
|
||||||
if (amr_oa_bwe_convert_indicated(conn_src->end.codec)) {
|
if (amr_oa_bwe_convert_indicated(conn_src->end.codec)) {
|
||||||
if (amr_oa_check(buf, len) != conn_src->end.codec->param.amr_octet_aligned)
|
int oa = amr_oa_check((char*)msg->data, msg->len);
|
||||||
|
if (oa < 0)
|
||||||
|
return -1;
|
||||||
|
if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check if the origin of the RTP packet seems plausible */
|
||||||
|
if (!tcfg->rtp_accept_all && check_rtp_origin(conn_src, from_addr))
|
||||||
|
return -1;
|
||||||
|
|
||||||
/* Execute endpoint specific implementation that handles the
|
/* Execute endpoint specific implementation that handles the
|
||||||
* dispatching of the RTP data */
|
* dispatching of the RTP data */
|
||||||
return endp->type->dispatch_rtp_cb(proto, &addr, buf, len,
|
return conn->endp->type->dispatch_rtp_cb(msg);
|
||||||
conn_src->conn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! set IP Type of Service parameter.
|
/*! set IP Type of Service parameter.
|
||||||
|
@@ -234,13 +234,8 @@ static void scheduled_from_osmux_tx_rtp_cb(struct msgb *msg, void *data)
|
|||||||
{
|
{
|
||||||
struct mgcp_conn_rtp *conn = data;
|
struct mgcp_conn_rtp *conn = data;
|
||||||
struct mgcp_endpoint *endp = conn->conn->endp;
|
struct mgcp_endpoint *endp = conn->conn->endp;
|
||||||
struct sockaddr_in addr = {
|
|
||||||
.sin_addr = conn->end.addr,
|
|
||||||
.sin_port = conn->end.rtp_port,
|
|
||||||
}; /* FIXME: not set/used in cb */
|
|
||||||
|
|
||||||
|
endp->type->dispatch_rtp_cb(msg);
|
||||||
endp->type->dispatch_rtp_cb(MGCP_PROTO_RTP, &addr, (char *)msg->data, msg->len, conn->conn);
|
|
||||||
msgb_free(msg);
|
msgb_free(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -244,6 +244,12 @@ static const struct log_info_cat log_categories[] = {
|
|||||||
.color = "\033[1;30m",
|
.color = "\033[1;30m",
|
||||||
.enabled = 1,.loglevel = LOGL_NOTICE,
|
.enabled = 1,.loglevel = LOGL_NOTICE,
|
||||||
},
|
},
|
||||||
|
[DIUUP] = {
|
||||||
|
.name = "DIUUP",
|
||||||
|
.description = "IuUP within RTP stream handling",
|
||||||
|
.color = "\033[1;31m",
|
||||||
|
.enabled = 1,.loglevel = LOGL_NOTICE,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct log_info log_info = {
|
const struct log_info log_info = {
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
SUBDIRS = \
|
SUBDIRS = \
|
||||||
mgcp_client \
|
mgcp_client \
|
||||||
mgcp \
|
mgcp \
|
||||||
|
iuup \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
|
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
|
||||||
|
45
tests/iuup/Makefile.am
Normal file
45
tests/iuup/Makefile.am
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
AM_CPPFLAGS = \
|
||||||
|
$(all_includes) \
|
||||||
|
-I$(top_srcdir)/include \
|
||||||
|
-I$(top_srcdir) \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
AM_CFLAGS = \
|
||||||
|
-Wall \
|
||||||
|
-ggdb3 \
|
||||||
|
$(LIBOSMOCORE_CFLAGS) \
|
||||||
|
$(LIBOSMOVTY_CFLAGS) \
|
||||||
|
$(LIBOSMOGSM_CFLAGS) \
|
||||||
|
$(LIBOSMONETIF_CFLAGS) \
|
||||||
|
$(COVERAGE_CFLAGS) \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
AM_LDFLAGS = \
|
||||||
|
$(COVERAGE_LDFLAGS) \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
EXTRA_DIST = \
|
||||||
|
iuup_test.ok \
|
||||||
|
iuup_test.err \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
noinst_PROGRAMS = \
|
||||||
|
iuup_test \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
iuup_test_SOURCES = \
|
||||||
|
iuup_test.c \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
iuup_test_LDADD = \
|
||||||
|
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
|
||||||
|
$(LIBOSMOCORE_LIBS) \
|
||||||
|
$(LIBOSMOVTY_LIBS) \
|
||||||
|
$(LIBOSMOGSM_LIBS) \
|
||||||
|
$(LIBRARY_DL) \
|
||||||
|
$(LIBOSMONETIF_LIBS) \
|
||||||
|
-lm \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
update_exp:
|
||||||
|
$(builddir)/iuup_test >$(srcdir)/iuup_test.ok 2>$(srcdir)/iuup_test.err
|
156
tests/iuup/iuup_test.c
Normal file
156
tests/iuup/iuup_test.c
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/msgb.h>
|
||||||
|
#include <osmocom/core/application.h>
|
||||||
|
#include <osmocom/core/logging.h>
|
||||||
|
|
||||||
|
#include <osmocom/mgcp/iuup_cn_node.h>
|
||||||
|
#include <osmocom/mgcp/iuup_protocol.h>
|
||||||
|
|
||||||
|
void *ctx = NULL;
|
||||||
|
|
||||||
|
static const char *dump(struct msgb *msg)
|
||||||
|
{
|
||||||
|
return osmo_hexdump_nospc(msg->data, msg->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct msgb *msgb_from_hex(const char *label, const char *hex)
|
||||||
|
{
|
||||||
|
struct msgb *msg = msgb_alloc_headroom(4096 + OSMO_IUUP_HEADROOM,
|
||||||
|
OSMO_IUUP_HEADROOM, label);
|
||||||
|
unsigned char *rc;
|
||||||
|
msg->l2h = msg->data;
|
||||||
|
rc = msgb_put(msg, osmo_hexparse(hex, msg->data, msgb_tailroom(msg)));
|
||||||
|
OSMO_ASSERT(rc == msg->l2h);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *expect_rx_payload = NULL;
|
||||||
|
int rx_payload(struct msgb *msg, void *node_priv)
|
||||||
|
{
|
||||||
|
printf("rx_payload() invoked by iuup_cn!\n");
|
||||||
|
printf(" [IuUP] -RTP->\n");
|
||||||
|
printf("%s\n", dump(msg));
|
||||||
|
printf("node_priv=%p\n", node_priv);
|
||||||
|
if (!expect_rx_payload) {
|
||||||
|
printf("ERROR: did not expect rx_payload()\n");
|
||||||
|
exit(-1);
|
||||||
|
} else if (strcmp(expect_rx_payload, dump(msg))) {
|
||||||
|
printf("ERROR: mismatches expected msg %s\n", expect_rx_payload);
|
||||||
|
exit(-1);
|
||||||
|
} else
|
||||||
|
printf("ok: matches expected msg\n");
|
||||||
|
expect_rx_payload = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *expect_tx_msg = NULL;
|
||||||
|
int tx_msg(struct msgb *msg, void *node_priv)
|
||||||
|
{
|
||||||
|
printf("tx_msg() invoked by iuup_cn!\n");
|
||||||
|
printf(" <-PDU- [IuUP]\n");
|
||||||
|
printf("%s\n", dump(msg));
|
||||||
|
printf("node_priv=%p\n", node_priv);
|
||||||
|
if (!expect_tx_msg) {
|
||||||
|
printf("ERROR: did not expect tx_msg()\n");
|
||||||
|
exit(-1);
|
||||||
|
} else if (strcmp(expect_tx_msg, dump(msg))) {
|
||||||
|
printf("ERROR: mismatches expected msg %s\n", expect_tx_msg);
|
||||||
|
exit(-1);
|
||||||
|
} else
|
||||||
|
printf("ok: matches expected msg\n");
|
||||||
|
expect_tx_msg = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rx_pdu(struct osmo_iuup_cn *cn, struct msgb *msg)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
printf(" -PDU-> [IuUP]\n");
|
||||||
|
printf("%s\n", dump(msg));
|
||||||
|
rc = osmo_iuup_cn_rx_pdu(cn, msg);
|
||||||
|
printf("rc=%d\n", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tx_payload(struct osmo_iuup_cn *cn, struct msgb *msg)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
printf(" [IuUP] <-RTP-\n");
|
||||||
|
printf("%s\n", dump(msg));
|
||||||
|
rc = osmo_iuup_cn_tx_payload(cn, msg);
|
||||||
|
printf("rc=%d\n", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_cn_session()
|
||||||
|
{
|
||||||
|
void *node_priv = (void*)0x2342;
|
||||||
|
|
||||||
|
struct osmo_iuup_cn_cfg cfg = {
|
||||||
|
.node_priv = node_priv,
|
||||||
|
.rx_payload = rx_payload,
|
||||||
|
.tx_msg = tx_msg,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct osmo_iuup_cn *cn = osmo_iuup_cn_init(ctx, &cfg, __func__);
|
||||||
|
OSMO_ASSERT(cn);
|
||||||
|
|
||||||
|
printf("\nSend IuUP Initialization. Expecting direct tx_msg() of the Initialization Ack\n");
|
||||||
|
expect_tx_msg = "8060dc5219495e3f00010111" /* RTP header */
|
||||||
|
"e4002400"; /* IuUP Init Ack */
|
||||||
|
rx_pdu(cn,
|
||||||
|
msgb_from_hex("IuUP-Init",
|
||||||
|
"8060dc5219495e3f00010111" /* <- RTP header */
|
||||||
|
"e000df99" /* <- IuUP header */
|
||||||
|
"160051673c01270000820000001710000100" /* IuUP params */));
|
||||||
|
|
||||||
|
#define RTP_HEADER "8060944c6256042c00010102"
|
||||||
|
#define IUUP_HEADER "0100e2b3"
|
||||||
|
#define RTP_PAYLOAD "6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0"
|
||||||
|
printf("\nReceive payload encapsulated in IuUP. Expecting rx_payload() of just RTP packet\n");
|
||||||
|
printf("i.e. should strip away " IUUP_HEADER "\n");
|
||||||
|
expect_rx_payload = RTP_HEADER "f03c" RTP_PAYLOAD;
|
||||||
|
rx_pdu(cn,
|
||||||
|
msgb_from_hex("IuUP-Data",
|
||||||
|
RTP_HEADER IUUP_HEADER RTP_PAYLOAD));
|
||||||
|
|
||||||
|
printf("\nTransmit RTP. Expecting tx_msg() with inserted IuUP header\n");
|
||||||
|
expect_tx_msg = RTP_HEADER "000002b3" RTP_PAYLOAD;
|
||||||
|
tx_payload(cn,
|
||||||
|
msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
|
||||||
|
|
||||||
|
printf("\nMore RTP, each time the Frame Nr advances, causing a new header CRC.\n");
|
||||||
|
expect_tx_msg = RTP_HEADER "0100e2b3" RTP_PAYLOAD;
|
||||||
|
tx_payload(cn,
|
||||||
|
msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
|
||||||
|
expect_tx_msg = RTP_HEADER "02007eb3" RTP_PAYLOAD;
|
||||||
|
tx_payload(cn,
|
||||||
|
msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
|
||||||
|
expect_tx_msg = RTP_HEADER "03009eb3" RTP_PAYLOAD;
|
||||||
|
tx_payload(cn,
|
||||||
|
msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
|
||||||
|
|
||||||
|
printf("All done.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct log_info_cat log_categories[] = {
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct log_info log_info = {
|
||||||
|
.cat = log_categories,
|
||||||
|
.num_cat = ARRAY_SIZE(log_categories),
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
ctx = talloc_named_const(NULL, 0, __FILE__);
|
||||||
|
void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0);
|
||||||
|
osmo_init_logging2(ctx, &log_info);
|
||||||
|
|
||||||
|
test_cn_session();
|
||||||
|
|
||||||
|
talloc_free(msgb_ctx);
|
||||||
|
return 0;
|
||||||
|
}
|
0
tests/iuup/iuup_test.err
Normal file
0
tests/iuup/iuup_test.err
Normal file
58
tests/iuup/iuup_test.ok
Normal file
58
tests/iuup/iuup_test.ok
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
|
||||||
|
Send IuUP Initialization. Expecting direct tx_msg() of the Initialization Ack
|
||||||
|
-PDU-> [IuUP]
|
||||||
|
8060dc5219495e3f00010111e000df99160051673c01270000820000001710000100
|
||||||
|
tx_msg() invoked by iuup_cn!
|
||||||
|
<-PDU- [IuUP]
|
||||||
|
8060dc5219495e3f00010111e4002400
|
||||||
|
node_priv=0x2342
|
||||||
|
ok: matches expected msg
|
||||||
|
rc=0
|
||||||
|
|
||||||
|
Receive payload encapsulated in IuUP. Expecting rx_payload() of just RTP packet
|
||||||
|
i.e. should strip away 0100e2b3
|
||||||
|
-PDU-> [IuUP]
|
||||||
|
8060944c6256042c000101020100e2b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||||
|
rx_payload() invoked by iuup_cn!
|
||||||
|
[IuUP] -RTP->
|
||||||
|
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||||
|
node_priv=0x2342
|
||||||
|
ok: matches expected msg
|
||||||
|
rc=0
|
||||||
|
|
||||||
|
Transmit RTP. Expecting tx_msg() with inserted IuUP header
|
||||||
|
[IuUP] <-RTP-
|
||||||
|
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||||
|
tx_msg() invoked by iuup_cn!
|
||||||
|
<-PDU- [IuUP]
|
||||||
|
8060944c6256042c00010102000002b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||||
|
node_priv=0x2342
|
||||||
|
ok: matches expected msg
|
||||||
|
rc=0
|
||||||
|
|
||||||
|
More RTP, each time the Frame Nr advances, causing a new header CRC.
|
||||||
|
[IuUP] <-RTP-
|
||||||
|
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||||
|
tx_msg() invoked by iuup_cn!
|
||||||
|
<-PDU- [IuUP]
|
||||||
|
8060944c6256042c000101020100e2b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||||
|
node_priv=0x2342
|
||||||
|
ok: matches expected msg
|
||||||
|
rc=0
|
||||||
|
[IuUP] <-RTP-
|
||||||
|
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||||
|
tx_msg() invoked by iuup_cn!
|
||||||
|
<-PDU- [IuUP]
|
||||||
|
8060944c6256042c0001010202007eb36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||||
|
node_priv=0x2342
|
||||||
|
ok: matches expected msg
|
||||||
|
rc=0
|
||||||
|
[IuUP] <-RTP-
|
||||||
|
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||||
|
tx_msg() invoked by iuup_cn!
|
||||||
|
<-PDU- [IuUP]
|
||||||
|
8060944c6256042c0001010203009eb36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
|
||||||
|
node_priv=0x2342
|
||||||
|
ok: matches expected msg
|
||||||
|
rc=0
|
||||||
|
All done.
|
@@ -28,6 +28,7 @@
|
|||||||
#include <osmocom/mgcp/mgcp_endp.h>
|
#include <osmocom/mgcp/mgcp_endp.h>
|
||||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||||
#include <osmocom/mgcp/mgcp_codec.h>
|
#include <osmocom/mgcp/mgcp_codec.h>
|
||||||
|
#include <osmocom/mgcp/mgcp_internal.h>
|
||||||
|
|
||||||
#include <osmocom/core/application.h>
|
#include <osmocom/core/application.h>
|
||||||
#include <osmocom/core/talloc.h>
|
#include <osmocom/core/talloc.h>
|
||||||
@@ -1237,7 +1238,7 @@ struct rtp_packet_info test_rtp_packets1[] = {
|
|||||||
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||||
struct mgcp_rtp_state *state,
|
struct mgcp_rtp_state *state,
|
||||||
struct mgcp_rtp_end *rtp_end,
|
struct mgcp_rtp_end *rtp_end,
|
||||||
struct sockaddr_in *addr, char *data, int len);
|
struct sockaddr_in *addr, struct msgb *msg);
|
||||||
|
|
||||||
static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||||
{
|
{
|
||||||
@@ -1249,7 +1250,6 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
|||||||
struct mgcp_rtp_state state;
|
struct mgcp_rtp_state state;
|
||||||
struct mgcp_rtp_end *rtp;
|
struct mgcp_rtp_end *rtp;
|
||||||
struct sockaddr_in addr = { 0 };
|
struct sockaddr_in addr = { 0 };
|
||||||
char buffer[4096];
|
|
||||||
uint32_t last_ssrc = 0;
|
uint32_t last_ssrc = 0;
|
||||||
uint32_t last_timestamp = 0;
|
uint32_t last_timestamp = 0;
|
||||||
uint32_t last_seqno = 0;
|
uint32_t last_seqno = 0;
|
||||||
@@ -1297,16 +1297,17 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
|||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
|
for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
|
||||||
struct rtp_packet_info *info = test_rtp_packets1 + i;
|
struct rtp_packet_info *info = test_rtp_packets1 + i;
|
||||||
|
struct msgb *msg = msgb_alloc(4096, __func__);
|
||||||
|
|
||||||
force_monotonic_time_us = round(1000000.0 * info->txtime);
|
force_monotonic_time_us = round(1000000.0 * info->txtime);
|
||||||
|
|
||||||
OSMO_ASSERT(info->len <= sizeof(buffer));
|
OSMO_ASSERT(info->len <= msgb_tailroom(msg));
|
||||||
OSMO_ASSERT(info->len >= 0);
|
OSMO_ASSERT(info->len >= 0);
|
||||||
memmove(buffer, info->data, info->len);
|
msg->l3h = msgb_put(msg, info->len);
|
||||||
|
memcpy((char*)msgb_l3(msg), info->data, info->len);
|
||||||
mgcp_rtp_end_config(&endp, 1, rtp);
|
mgcp_rtp_end_config(&endp, 1, rtp);
|
||||||
|
|
||||||
mgcp_patch_and_count(&endp, &state, rtp, &addr,
|
mgcp_patch_and_count(&endp, &state, rtp, &addr, msg);
|
||||||
buffer, info->len);
|
|
||||||
|
|
||||||
if (state.out_stream.ssrc != last_ssrc) {
|
if (state.out_stream.ssrc != last_ssrc) {
|
||||||
printf("Output SSRC changed to %08x\n",
|
printf("Output SSRC changed to %08x\n",
|
||||||
@@ -1333,6 +1334,8 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
|||||||
last_out_ts_err_cnt = state.out_stream.err_ts_ctr->current;
|
last_out_ts_err_cnt = state.out_stream.err_ts_ctr->current;
|
||||||
last_timestamp = state.out_stream.last_timestamp;
|
last_timestamp = state.out_stream.last_timestamp;
|
||||||
last_seqno = state.out_stream.last_seq;
|
last_seqno = state.out_stream.last_seq;
|
||||||
|
|
||||||
|
msgb_free(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
force_monotonic_time_us = -1;
|
force_monotonic_time_us = -1;
|
||||||
@@ -1711,98 +1714,335 @@ static void test_check_local_cx_options(void *ctx)
|
|||||||
OSMO_ASSERT(check_local_cx_options(ctx, ",,,") == -1);
|
OSMO_ASSERT(check_local_cx_options(ctx, ",,,") == -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_mgcp_codec_pt_translate_pars(struct mgcp_rtp_codec *c)
|
static const struct mgcp_codec_param amr_param_octet_aligned_true = {
|
||||||
{
|
.amr_octet_aligned_present = true,
|
||||||
c->rate = 8000;
|
.amr_octet_aligned = true,
|
||||||
c->channels = 1;
|
};
|
||||||
c->frame_duration_num = 23;
|
|
||||||
c->frame_duration_den = 42;
|
static const struct mgcp_codec_param amr_param_octet_aligned_false = {
|
||||||
}
|
.amr_octet_aligned_present = true,
|
||||||
|
.amr_octet_aligned = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct mgcp_codec_param amr_param_octet_aligned_unset = {
|
||||||
|
.amr_octet_aligned_present = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct testcase_mgcp_codec_pt_translate_codec {
|
||||||
|
int payload_type;
|
||||||
|
const char *audio_name;
|
||||||
|
const struct mgcp_codec_param *param;
|
||||||
|
int expect_rc;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct testcase_mgcp_codec_pt_translate_expect {
|
||||||
|
bool end;
|
||||||
|
int payload_type_map[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct testcase_mgcp_codec_pt_translate {
|
||||||
|
const char *descr;
|
||||||
|
/* two conns on an endpoint, each with N configured codecs */
|
||||||
|
struct testcase_mgcp_codec_pt_translate_codec codecs[2][10];
|
||||||
|
struct testcase_mgcp_codec_pt_translate_expect expect[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translate_cases[] = {
|
||||||
|
{
|
||||||
|
.descr = "same order, but differing payload type numbers",
|
||||||
|
.codecs = {
|
||||||
|
{
|
||||||
|
{ 112, "AMR/8000/1", &amr_param_octet_aligned_true, },
|
||||||
|
{ 0, "PCMU/8000/1", NULL, },
|
||||||
|
{ 111, "GSM-HR-08/8000/1", NULL, },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{ 96, "AMR/8000/1", &amr_param_octet_aligned_true, },
|
||||||
|
{ 0, "PCMU/8000/1", NULL, },
|
||||||
|
{ 97, "GSM-HR-08/8000/1", NULL, },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.expect = {
|
||||||
|
{ .payload_type_map = {112, 96}, },
|
||||||
|
{ .payload_type_map = {0, 0}, },
|
||||||
|
{ .payload_type_map = {111, 97} },
|
||||||
|
{ .payload_type_map = {123, -EINVAL} },
|
||||||
|
{ .end = true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.descr = "different order and different payload type numbers",
|
||||||
|
.codecs = {
|
||||||
|
{
|
||||||
|
{ 0, "PCMU/8000/1", NULL, },
|
||||||
|
{ 111, "GSM-HR-08/8000/1", NULL, },
|
||||||
|
{ 112, "AMR/8000/1", &amr_param_octet_aligned_true, },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{ 97, "GSM-HR-08/8000/1", NULL, },
|
||||||
|
{ 0, "PCMU/8000/1", NULL, },
|
||||||
|
{ 96, "AMR/8000/1", &amr_param_octet_aligned_true, },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.expect = {
|
||||||
|
{ .payload_type_map = {112, 96}, },
|
||||||
|
{ .payload_type_map = {0, 0}, },
|
||||||
|
{ .payload_type_map = {111, 97} },
|
||||||
|
{ .payload_type_map = {123, -EINVAL} },
|
||||||
|
{ .end = true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.descr = "both sides have the same payload_type numbers assigned to differing codecs",
|
||||||
|
.codecs = {
|
||||||
|
{
|
||||||
|
{ 0, "PCMU/8000/1", NULL, },
|
||||||
|
{ 96, "GSM-HR-08/8000/1", NULL, },
|
||||||
|
{ 97, "AMR/8000/1", &amr_param_octet_aligned_true, },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{ 97, "GSM-HR-08/8000/1", NULL, },
|
||||||
|
{ 0, "PCMU/8000/1", NULL, },
|
||||||
|
{ 96, "AMR/8000/1", &amr_param_octet_aligned_true, },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.expect = {
|
||||||
|
{ .payload_type_map = {96, 97}, },
|
||||||
|
{ .payload_type_map = {97, 96}, },
|
||||||
|
{ .payload_type_map = {0, 0}, },
|
||||||
|
{ .end = true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.descr = "conn0 has no codecs",
|
||||||
|
.codecs = {
|
||||||
|
{
|
||||||
|
/* no codecs */
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{ 96, "AMR/8000/1", &amr_param_octet_aligned_true, },
|
||||||
|
{ 0, "PCMU/8000/1", NULL, },
|
||||||
|
{ 97, "GSM-HR-08/8000/1", NULL, },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.expect = {
|
||||||
|
{ .payload_type_map = {112, -EINVAL}, },
|
||||||
|
{ .payload_type_map = {0, -EINVAL}, },
|
||||||
|
{ .payload_type_map = {111, -EINVAL} },
|
||||||
|
{ .end = true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.descr = "conn1 has no codecs",
|
||||||
|
.codecs = {
|
||||||
|
{
|
||||||
|
{ 112, "AMR/8000/1", &amr_param_octet_aligned_true, },
|
||||||
|
{ 0, "PCMU/8000/1", NULL, },
|
||||||
|
{ 111, "GSM-HR-08/8000/1", NULL, },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
/* no codecs */
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.expect = {
|
||||||
|
{ .payload_type_map = {112, -EINVAL}, },
|
||||||
|
{ .payload_type_map = {0, -EINVAL}, },
|
||||||
|
{ .payload_type_map = {111, -EINVAL} },
|
||||||
|
{ .end = true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.descr = "test AMR with differing octet-aligned settings",
|
||||||
|
.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} },
|
||||||
|
{ .end = true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.descr = "test AMR with missing octet-aligned settings (defaults to 0)",
|
||||||
|
.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} },
|
||||||
|
{ .end = true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.descr = "test AMR with NULL param (defaults to 0)",
|
||||||
|
.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} },
|
||||||
|
{ .end = true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.descr = "match FOO/8000/1 and FOO/8000 as identical, single channel is implicit",
|
||||||
|
.codecs = {
|
||||||
|
{
|
||||||
|
{ 0, "PCMU/8000/1", NULL, },
|
||||||
|
{ 111, "GSM-HR-08/8000/1", NULL, },
|
||||||
|
{ 112, "AMR/8000/1", &amr_param_octet_aligned_true, },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{ 97, "GSM-HR-08/8000", NULL, },
|
||||||
|
{ 0, "PCMU/8000", NULL, },
|
||||||
|
{ 96, "AMR/8000", &amr_param_octet_aligned_true, },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.expect = {
|
||||||
|
{ .payload_type_map = {112, 96}, },
|
||||||
|
{ .payload_type_map = {0, 0}, },
|
||||||
|
{ .payload_type_map = {111, 97} },
|
||||||
|
{ .payload_type_map = {123, -EINVAL} },
|
||||||
|
{ .end = true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.descr = "match FOO/8000/1 and FOO as identical, 8k and single channel are implicit",
|
||||||
|
.codecs = {
|
||||||
|
{
|
||||||
|
{ 0, "PCMU/8000/1", NULL, },
|
||||||
|
{ 111, "GSM-HR-08/8000/1", NULL, },
|
||||||
|
{ 112, "AMR/8000/1", &amr_param_octet_aligned_true, },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{ 97, "GSM-HR-08", NULL, },
|
||||||
|
{ 0, "PCMU", NULL, },
|
||||||
|
{ 96, "AMR", &amr_param_octet_aligned_true, },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.expect = {
|
||||||
|
{ .payload_type_map = {112, 96}, },
|
||||||
|
{ .payload_type_map = {0, 0}, },
|
||||||
|
{ .payload_type_map = {111, 97} },
|
||||||
|
{ .payload_type_map = {123, -EINVAL} },
|
||||||
|
{ .end = true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.descr = "test whether channel number matching is waterproof",
|
||||||
|
.codecs = {
|
||||||
|
{
|
||||||
|
{ 111, "GSM-HR-08/8000", },
|
||||||
|
{ 112, "GSM-HR-08/8000/2", .expect_rc = -22},
|
||||||
|
{ 113, "GSM-HR-08/8000/3", .expect_rc = -22},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{ 122, "GSM-HR-08/8000/2", .expect_rc = -22},
|
||||||
|
{ 121, "GSM-HR-08/8000/1", },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.expect = {
|
||||||
|
{ .payload_type_map = {111, 121}, },
|
||||||
|
{ .payload_type_map = {112, -EINVAL} },
|
||||||
|
{ .payload_type_map = {113, -EINVAL} },
|
||||||
|
{ .end = true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
static void test_mgcp_codec_pt_translate(void)
|
static void test_mgcp_codec_pt_translate(void)
|
||||||
{
|
{
|
||||||
struct mgcp_conn_rtp conn_src;
|
int i;
|
||||||
struct mgcp_conn_rtp conn_dst;
|
bool ok = true;
|
||||||
int pt_dst;
|
printf("\nTesting mgcp_codec_pt_translate()\n");
|
||||||
|
|
||||||
/* Setup a realistic set of codec configurations on both
|
for (i = 0; i < ARRAY_SIZE(test_mgcp_codec_pt_translate_cases); i++) {
|
||||||
* ends. AMR and HR will use different payload types. PCMU
|
const struct testcase_mgcp_codec_pt_translate *t = &test_mgcp_codec_pt_translate_cases[i];
|
||||||
* must use 0 on both ends since this is not a dynamic payload
|
struct mgcp_conn_rtp conn[2] = {};
|
||||||
* type */
|
int rc;
|
||||||
test_mgcp_codec_pt_translate_pars(&conn_src.end.codecs[0]);
|
int conn_i;
|
||||||
test_mgcp_codec_pt_translate_pars(&conn_dst.end.codecs[0]);
|
int c;
|
||||||
test_mgcp_codec_pt_translate_pars(&conn_src.end.codecs[1]);
|
|
||||||
test_mgcp_codec_pt_translate_pars(&conn_dst.end.codecs[1]);
|
|
||||||
test_mgcp_codec_pt_translate_pars(&conn_src.end.codecs[2]);
|
|
||||||
test_mgcp_codec_pt_translate_pars(&conn_dst.end.codecs[2]);
|
|
||||||
conn_src.end.codecs[0].payload_type = 112;
|
|
||||||
conn_dst.end.codecs[0].payload_type = 96;
|
|
||||||
conn_src.end.codecs[1].payload_type = 0;
|
|
||||||
conn_dst.end.codecs[1].payload_type = 0;
|
|
||||||
conn_src.end.codecs[2].payload_type = 111;
|
|
||||||
conn_dst.end.codecs[2].payload_type = 97;
|
|
||||||
conn_src.end.codecs[0].audio_name = "AMR/8000/1";
|
|
||||||
conn_dst.end.codecs[0].audio_name = "AMR/8000/1";
|
|
||||||
conn_src.end.codecs[1].audio_name = "PCMU/8000/1";
|
|
||||||
conn_dst.end.codecs[1].audio_name = "PCMU/8000/1";
|
|
||||||
conn_src.end.codecs[2].audio_name = "GSM-HR-08/8000/1";
|
|
||||||
conn_dst.end.codecs[2].audio_name = "GSM-HR-08/8000/1";
|
|
||||||
conn_src.end.codecs[0].subtype_name = "AMR";
|
|
||||||
conn_dst.end.codecs[0].subtype_name = "AMR";
|
|
||||||
conn_src.end.codecs[1].subtype_name = "PCMU";
|
|
||||||
conn_dst.end.codecs[1].subtype_name = "PCMU";
|
|
||||||
conn_src.end.codecs[2].subtype_name = "GSM-HR-08";
|
|
||||||
conn_dst.end.codecs[2].subtype_name = "GSM-HR-08";
|
|
||||||
conn_src.end.codecs_assigned = 3;
|
|
||||||
conn_dst.end.codecs_assigned = 3;
|
|
||||||
|
|
||||||
/* We expect the function to find the PT we must use when we send the
|
printf("#%d: %s\n", i, t->descr);
|
||||||
* packet out to the destination. All we know is the context for both
|
|
||||||
* connections and the payload type from the source packet */
|
|
||||||
pt_dst =
|
|
||||||
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
|
||||||
conn_src.end.codecs[0].payload_type);
|
|
||||||
OSMO_ASSERT(pt_dst == conn_dst.end.codecs[0].payload_type);
|
|
||||||
pt_dst =
|
|
||||||
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
|
||||||
conn_src.end.codecs[1].payload_type);
|
|
||||||
OSMO_ASSERT(pt_dst == conn_dst.end.codecs[1].payload_type);
|
|
||||||
pt_dst =
|
|
||||||
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
|
||||||
conn_src.end.codecs[2].payload_type);
|
|
||||||
OSMO_ASSERT(pt_dst == conn_dst.end.codecs[2].payload_type);
|
|
||||||
|
|
||||||
/* Try some constellations that must fail */
|
for (conn_i = 0; conn_i < 2; conn_i++) {
|
||||||
pt_dst = mgcp_codec_pt_translate(&conn_src, &conn_dst, 123);
|
printf(" - add codecs on conn%d:\n", conn_i);
|
||||||
OSMO_ASSERT(pt_dst == -EINVAL);
|
for (c = 0; c < ARRAY_SIZE(t->codecs[conn_i]); c++) {
|
||||||
conn_src.end.codecs_assigned = 0;
|
const struct testcase_mgcp_codec_pt_translate_codec *codec = &t->codecs[conn_i][c];
|
||||||
conn_dst.end.codecs_assigned = 3;
|
if (!codec->audio_name)
|
||||||
pt_dst =
|
break;
|
||||||
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
|
||||||
conn_src.end.codecs[0].payload_type);
|
rc = mgcp_codec_add(&conn[conn_i], codec->payload_type, codec->audio_name, codec->param);
|
||||||
OSMO_ASSERT(pt_dst == -EINVAL);
|
|
||||||
pt_dst =
|
printf(" %2d: %3d %s%s -> rc=%d\n", c, codec->payload_type, codec->audio_name,
|
||||||
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
codec->param ?
|
||||||
conn_src.end.codecs[1].payload_type);
|
(codec->param->amr_octet_aligned_present?
|
||||||
OSMO_ASSERT(pt_dst == -EINVAL);
|
(codec->param->amr_octet_aligned ?
|
||||||
pt_dst =
|
" octet-aligned=1" : " octet-aligned=0")
|
||||||
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
: " octet-aligned=unset")
|
||||||
conn_src.end.codecs[2].payload_type);
|
: "",
|
||||||
OSMO_ASSERT(pt_dst == -EINVAL);
|
rc);
|
||||||
conn_src.end.codecs_assigned = 3;
|
if (rc != codec->expect_rc) {
|
||||||
conn_dst.end.codecs_assigned = 0;
|
printf(" ERROR: expected rc=%d\n", codec->expect_rc);
|
||||||
pt_dst =
|
ok = false;
|
||||||
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
}
|
||||||
conn_src.end.codecs[0].payload_type);
|
}
|
||||||
OSMO_ASSERT(pt_dst == -EINVAL);
|
if (!c)
|
||||||
pt_dst =
|
printf(" (none)\n");
|
||||||
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
}
|
||||||
conn_src.end.codecs[1].payload_type);
|
|
||||||
OSMO_ASSERT(pt_dst == -EINVAL);
|
for (c = 0; c < ARRAY_SIZE(t->expect); c++) {
|
||||||
pt_dst =
|
const struct testcase_mgcp_codec_pt_translate_expect *expect = &t->expect[c];
|
||||||
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
int result;
|
||||||
conn_src.end.codecs[2].payload_type);
|
|
||||||
OSMO_ASSERT(pt_dst == -EINVAL);
|
if (expect->end)
|
||||||
|
break;
|
||||||
|
|
||||||
|
result = mgcp_codec_pt_translate(&conn[0], &conn[1], expect->payload_type_map[0]);
|
||||||
|
printf(" - mgcp_codec_pt_translate(conn0, conn1, %d) -> %d\n",
|
||||||
|
expect->payload_type_map[0], result);
|
||||||
|
if (result != expect->payload_type_map[1]) {
|
||||||
|
printf(" ERROR: expected -> %d\n", expect->payload_type_map[1]);
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the expected result is an error, don't do reverse map test */
|
||||||
|
if (expect->payload_type_map[1] < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
result = mgcp_codec_pt_translate(&conn[1], &conn[0], expect->payload_type_map[1]);
|
||||||
|
printf(" - mgcp_codec_pt_translate(conn1, conn0, %d) -> %d\n",
|
||||||
|
expect->payload_type_map[1], result);
|
||||||
|
if (result != expect->payload_type_map[0]) {
|
||||||
|
printf(" ERROR: expected -> %d\n", expect->payload_type_map[0]);
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (conn_i = 0; conn_i < 2; conn_i++)
|
||||||
|
mgcp_codec_reset_all(&conn[conn_i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
OSMO_ASSERT(ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_conn_id_matching()
|
void test_conn_id_matching()
|
||||||
|
@@ -1218,6 +1218,148 @@ p:10, a:PCMU -> p:10, a:PCMU
|
|||||||
Testing get_lco_identifier()
|
Testing get_lco_identifier()
|
||||||
p:10, a:PCMU -> p:10, a:PCMU
|
p:10, a:PCMU -> p:10, a:PCMU
|
||||||
p:10, a:PCMU -> p:10, a:PCMU
|
p:10, a:PCMU -> p:10, a:PCMU
|
||||||
|
'XXXX, p:10, a:PCMU' -> 'p:10, a:PCMU'
|
||||||
|
'XXXX,p:10,a:PCMU' -> 'p:10,a:PCMU'
|
||||||
|
'10,a:PCMU' -> 'a:PCMU'
|
||||||
|
'10, a:PCMU' -> 'a:PCMU'
|
||||||
|
'10,a: PCMU' -> 'a: PCMU'
|
||||||
|
'10 ,a: PCMU' -> 'a: PCMU'
|
||||||
|
', a:PCMU' -> 'a:PCMU'
|
||||||
|
' a:PCMU' -> 'a:PCMU'
|
||||||
|
'' -> '(null)'
|
||||||
|
p10, aPCMU -> (null)
|
||||||
|
'10,a :PCMU' -> '(null)'
|
||||||
|
|
||||||
|
Testing mgcp_codec_pt_translate()
|
||||||
|
#0: same order, but differing payload type numbers
|
||||||
|
- add codecs on conn0:
|
||||||
|
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:
|
||||||
|
0: 96 AMR/8000/1 octet-aligned=1 -> rc=0
|
||||||
|
1: 0 PCMU/8000/1 -> rc=0
|
||||||
|
2: 97 GSM-HR-08/8000/1 -> rc=0
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
|
||||||
|
- mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
|
||||||
|
- mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
|
||||||
|
- mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
|
||||||
|
#1: different order and different payload type numbers
|
||||||
|
- add codecs on conn0:
|
||||||
|
0: 0 PCMU/8000/1 -> rc=0
|
||||||
|
1: 111 GSM-HR-08/8000/1 -> rc=0
|
||||||
|
2: 112 AMR/8000/1 octet-aligned=1 -> rc=0
|
||||||
|
- add codecs on conn1:
|
||||||
|
0: 97 GSM-HR-08/8000/1 -> rc=0
|
||||||
|
1: 0 PCMU/8000/1 -> rc=0
|
||||||
|
2: 96 AMR/8000/1 octet-aligned=1 -> rc=0
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
|
||||||
|
- mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
|
||||||
|
- mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
|
||||||
|
- mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
|
||||||
|
#2: both sides have the same payload_type numbers assigned to differing codecs
|
||||||
|
- add codecs on conn0:
|
||||||
|
0: 0 PCMU/8000/1 -> rc=0
|
||||||
|
1: 96 GSM-HR-08/8000/1 -> rc=0
|
||||||
|
2: 97 AMR/8000/1 octet-aligned=1 -> rc=0
|
||||||
|
- add codecs on conn1:
|
||||||
|
0: 97 GSM-HR-08/8000/1 -> rc=0
|
||||||
|
1: 0 PCMU/8000/1 -> rc=0
|
||||||
|
2: 96 AMR/8000/1 octet-aligned=1 -> rc=0
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 96) -> 97
|
||||||
|
- mgcp_codec_pt_translate(conn1, conn0, 97) -> 96
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 97) -> 96
|
||||||
|
- mgcp_codec_pt_translate(conn1, conn0, 96) -> 97
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
|
||||||
|
- mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
|
||||||
|
#3: conn0 has no codecs
|
||||||
|
- add codecs on conn0:
|
||||||
|
(none)
|
||||||
|
- add codecs on conn1:
|
||||||
|
0: 96 AMR/8000/1 octet-aligned=1 -> rc=0
|
||||||
|
1: 0 PCMU/8000/1 -> rc=0
|
||||||
|
2: 97 GSM-HR-08/8000/1 -> rc=0
|
||||||
|
- 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
|
||||||
|
- add codecs on conn0:
|
||||||
|
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)
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 112) -> -22
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 0) -> -22
|
||||||
|
- 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
|
||||||
|
1: 112 AMR/8000 octet-aligned=0 -> rc=0
|
||||||
|
- 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) -> 121
|
||||||
|
- mgcp_codec_pt_translate(conn1, conn0, 121) -> 111
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 122
|
||||||
|
- mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
|
||||||
|
#6: test AMR with missing octet-aligned settings (defaults to 0)
|
||||||
|
- add codecs on conn0:
|
||||||
|
0: 111 AMR/8000 octet-aligned=1 -> rc=0
|
||||||
|
1: 112 AMR/8000 octet-aligned=0 -> rc=0
|
||||||
|
- add codecs on conn1:
|
||||||
|
0: 122 AMR/8000 octet-aligned=unset -> rc=0
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 122
|
||||||
|
- mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
|
||||||
|
#7: test AMR with NULL param (defaults to 0)
|
||||||
|
- add codecs on conn0:
|
||||||
|
0: 111 AMR/8000 octet-aligned=1 -> rc=0
|
||||||
|
1: 112 AMR/8000 octet-aligned=0 -> rc=0
|
||||||
|
- add codecs on conn1:
|
||||||
|
0: 122 AMR/8000 -> rc=0
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 111) -> -22
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 122
|
||||||
|
- mgcp_codec_pt_translate(conn1, conn0, 122) -> 112
|
||||||
|
#8: match FOO/8000/1 and FOO/8000 as identical, single channel is implicit
|
||||||
|
- add codecs on conn0:
|
||||||
|
0: 0 PCMU/8000/1 -> rc=0
|
||||||
|
1: 111 GSM-HR-08/8000/1 -> rc=0
|
||||||
|
2: 112 AMR/8000/1 octet-aligned=1 -> rc=0
|
||||||
|
- add codecs on conn1:
|
||||||
|
0: 97 GSM-HR-08/8000 -> rc=0
|
||||||
|
1: 0 PCMU/8000 -> rc=0
|
||||||
|
2: 96 AMR/8000 octet-aligned=1 -> rc=0
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
|
||||||
|
- mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
|
||||||
|
- mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
|
||||||
|
- mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
|
||||||
|
#9: match FOO/8000/1 and FOO as identical, 8k and single channel are implicit
|
||||||
|
- add codecs on conn0:
|
||||||
|
0: 0 PCMU/8000/1 -> rc=0
|
||||||
|
1: 111 GSM-HR-08/8000/1 -> rc=0
|
||||||
|
2: 112 AMR/8000/1 octet-aligned=1 -> rc=0
|
||||||
|
- add codecs on conn1:
|
||||||
|
0: 97 GSM-HR-08 -> rc=0
|
||||||
|
1: 0 PCMU -> rc=0
|
||||||
|
2: 96 AMR octet-aligned=1 -> rc=0
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 112) -> 96
|
||||||
|
- mgcp_codec_pt_translate(conn1, conn0, 96) -> 112
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 0) -> 0
|
||||||
|
- mgcp_codec_pt_translate(conn1, conn0, 0) -> 0
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 111) -> 97
|
||||||
|
- mgcp_codec_pt_translate(conn1, conn0, 97) -> 111
|
||||||
|
- mgcp_codec_pt_translate(conn0, conn1, 123) -> -22
|
||||||
|
#10: test whether channel number matching is waterproof
|
||||||
- add codecs on conn0:
|
- add codecs on conn0:
|
||||||
0: 111 GSM-HR-08/8000 -> rc=0
|
0: 111 GSM-HR-08/8000 -> rc=0
|
||||||
1: 112 GSM-HR-08/8000/2 -> rc=-22
|
1: 112 GSM-HR-08/8000/2 -> rc=-22
|
||||||
|
@@ -13,3 +13,10 @@ AT_KEYWORDS([mgcp])
|
|||||||
cat $abs_srcdir/mgcp/mgcp_test.ok > expout
|
cat $abs_srcdir/mgcp/mgcp_test.ok > expout
|
||||||
AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore])
|
AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore])
|
||||||
AT_CLEANUP
|
AT_CLEANUP
|
||||||
|
|
||||||
|
AT_SETUP([iuup])
|
||||||
|
AT_KEYWORDS([iuup])
|
||||||
|
cat $abs_srcdir/iuup/iuup_test.ok > expout
|
||||||
|
cat $abs_srcdir/iuup/iuup_test.err > experr
|
||||||
|
AT_CHECK([$abs_top_builddir/tests/iuup/iuup_test], [], [expout], [experr])
|
||||||
|
AT_CLEANUP
|
||||||
|
Reference in New Issue
Block a user