mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-10-23 08:12:01 +00:00
Compare commits
29 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
fbe05d8b1f | ||
|
832bcdf631 | ||
|
73f9c02f49 | ||
|
6a25a61142 | ||
|
1dbbed169a | ||
|
a8f27abe12 | ||
|
ca2aec0235 | ||
|
e827831514 | ||
|
923d60bb12 | ||
|
f2bf8dc8c8 | ||
|
055ded74de | ||
|
3ff71284fa | ||
|
cc0b97e197 | ||
|
8c69e29820 | ||
|
843d9038ce | ||
|
d071a30238 | ||
|
3ab8ca4d84 | ||
|
23f4048b57 | ||
|
401b740ccd | ||
|
a468b0f57f | ||
|
b0cfa7272e | ||
|
683e05f60b | ||
|
16b637bf1b | ||
|
2698540c1e | ||
|
d2f5e69d3e | ||
|
ce64f18587 | ||
|
667fa59b0c | ||
|
5a6220f43b | ||
|
7c6dd3c2c3 |
@@ -195,6 +195,7 @@ AC_OUTPUT(
|
||||
tests/atlocal
|
||||
tests/mgcp_client/Makefile
|
||||
tests/mgcp/Makefile
|
||||
tests/iuup/Makefile
|
||||
doc/Makefile
|
||||
doc/examples/Makefile
|
||||
doc/manuals/Makefile
|
||||
|
@@ -12,7 +12,7 @@ arguments:
|
||||
*-h, --help*::
|
||||
Print a short help message about the supported options
|
||||
*-V, --version*::
|
||||
Print the compile-time version number of the OsmoBTS program
|
||||
Print the compile-time version number of the program
|
||||
*-D, --daemonize*::
|
||||
Fork the process as a daemon into background.
|
||||
*-c, --config-file 'CONFIGFILE'*::
|
||||
|
@@ -7,4 +7,6 @@ noinst_HEADERS = \
|
||||
mgcp_sdp.h \
|
||||
mgcp_codec.h \
|
||||
debug.h \
|
||||
iuup_cn_node.h \
|
||||
iuup_protocol.h \
|
||||
$(NULL)
|
||||
|
@@ -29,6 +29,7 @@
|
||||
/* Debug Areas of the code */
|
||||
enum {
|
||||
DRTP,
|
||||
DIUUP,
|
||||
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_decide(struct mgcp_conn_rtp *conn);
|
||||
int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type);
|
||||
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
|
||||
const char *subtype_name, unsigned int match_nr);
|
||||
|
@@ -23,15 +23,27 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
struct sockaddr_in;
|
||||
struct mgcp_conn;
|
||||
struct mgcp_conn_rtp;
|
||||
struct mgcp_endpoint;
|
||||
|
||||
/* Callback type for RTP dispatcher functions
|
||||
(e.g mgcp_dispatch_rtp_bridge_cb, see below) */
|
||||
typedef int (*mgcp_dispatch_rtp_cb) (int proto, struct sockaddr_in *addr,
|
||||
char *buf, unsigned int buf_size,
|
||||
struct mgcp_conn *conn);
|
||||
struct osmo_rtp_msg_ctx {
|
||||
int proto;
|
||||
struct mgcp_conn_rtp *conn_src;
|
||||
struct sockaddr_in *from_addr;
|
||||
};
|
||||
|
||||
#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
|
||||
* is automatically executed when a connection is freed (see mgcp_conn_free()
|
||||
|
@@ -37,6 +37,13 @@
|
||||
#define CONN_ID_BTS "0"
|
||||
#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 {
|
||||
MGCP_TRUNK_VIRTUAL,
|
||||
MGCP_TRUNK_E1,
|
||||
@@ -204,6 +211,8 @@ struct mgcp_conn_rtp {
|
||||
} osmux;
|
||||
|
||||
struct rate_ctr_group *rate_ctr_group;
|
||||
|
||||
struct osmo_iuup_cn *iuup;
|
||||
};
|
||||
|
||||
/*! 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,
|
||||
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);
|
||||
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,
|
||||
unsigned int buf_size, struct mgcp_conn *conn);
|
||||
int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg);
|
||||
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,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
@@ -350,3 +358,8 @@ LOGP(cat, level, "endpoint:0x%x " fmt, \
|
||||
LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
|
||||
(conn)->id, \
|
||||
## 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);
|
||||
|
@@ -29,6 +29,9 @@ void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci,
|
||||
uint32_t event_success, uint32_t event_failure,
|
||||
void *notify_data);
|
||||
|
||||
void osmo_mgcpc_ep_cancel_notify(struct osmo_mgcpc_ep *ep, struct osmo_fsm_inst *notify);
|
||||
struct osmo_mgcpc_ep *osmo_mgcpc_ep_ci_ep(struct osmo_mgcpc_ep_ci *ci);
|
||||
|
||||
/*! Dispatch a DLCX for the given connection.
|
||||
* \param ci Connection identifier as obtained from osmo_mgcpc_ep_ci_add().
|
||||
*/
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com>
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
@@ -337,48 +337,34 @@ static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *li
|
||||
{
|
||||
unsigned int pt;
|
||||
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 (sscanf(line, "a=ptime:%u", &r->ptime) != 1)
|
||||
goto response_parse_failure_ptime;
|
||||
} else if (strstr(line, "rtpmap")) {
|
||||
if (sscanf(line, "a=rtpmap:%d %63s", &pt, codec_resp) == 2) {
|
||||
/* The MGW may assign an own payload type in the
|
||||
* response if the choosen codec falls into the IANA
|
||||
* assigned dynamic payload type range (96-127).
|
||||
* Normally the MGW should obey the 3gpp payload type
|
||||
* assignments, which are fixed, so we likely wont see
|
||||
* anything unexpected here. In order to be sure that
|
||||
* we will now check the codec string and if the result
|
||||
* does not match to what is IANA / 3gpp assigned, we
|
||||
* will create an entry in the ptmap table so we can
|
||||
* lookup later what has been assigned. */
|
||||
codec = map_str_to_codec(codec_resp);
|
||||
if (codec != pt) {
|
||||
if (r->ptmap_len < ARRAY_SIZE(r->ptmap)) {
|
||||
r->ptmap[r->ptmap_len].pt = pt;
|
||||
r->ptmap[r->ptmap_len].codec = codec;
|
||||
r->ptmap_len++;
|
||||
} else
|
||||
goto response_parse_failure_rtpmap;
|
||||
}
|
||||
|
||||
} else
|
||||
goto response_parse_failure_rtpmap;
|
||||
if (osmo_str_startswith(line, A_PTIME)) {
|
||||
if (sscanf(line, A_PTIME "%u", &r->ptime) != 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to parse SDP parameter, invalid ptime (%s)\n", line);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (osmo_str_startswith(line, A_RTPMAP)) {
|
||||
if (sscanf(line, A_RTPMAP "%d %63s", &pt, codec_resp) != 2) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to parse SDP parameter, invalid rtpmap: %s\n", osmo_quote_str(line, -1));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (r->ptmap_len >= ARRAY_SIZE(r->ptmap)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "No more space in ptmap array (len=%u)\n", r->ptmap_len);
|
||||
return -ENOSPC;
|
||||
}
|
||||
codec = map_str_to_codec(codec_resp);
|
||||
r->ptmap[r->ptmap_len].pt = pt;
|
||||
r->ptmap[r->ptmap_len].codec = codec;
|
||||
r->ptmap_len++;
|
||||
}
|
||||
|
||||
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" */
|
||||
@@ -480,9 +466,8 @@ int mgcp_response_parse_params(struct mgcp_response *r)
|
||||
/* Find beginning of the parameter (SDP) section */
|
||||
data_ptr = mgcp_find_section_end(data);
|
||||
if (!data_ptr) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"MGCP response: cannot find start of SDP parameters\n");
|
||||
rc = -EINVAL;
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "MGCP response contains no SDP parameters\n");
|
||||
rc = 0;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
|
@@ -73,6 +73,14 @@ enum osmo_mgcpc_ep_fsm_event {
|
||||
|
||||
static struct osmo_fsm osmo_mgcpc_ep_fsm;
|
||||
|
||||
struct fsm_notify {
|
||||
struct llist_head entry;
|
||||
struct osmo_fsm_inst *fi;
|
||||
uint32_t success;
|
||||
uint32_t failure;
|
||||
void *data;
|
||||
};
|
||||
|
||||
/*! One connection on an endpoint, corresponding to a connection identifier (CI) as returned by the MGW.
|
||||
* An endpoint has a fixed number of slots of these, which may or may not be in use.
|
||||
*/
|
||||
@@ -87,10 +95,7 @@ struct osmo_mgcpc_ep_ci {
|
||||
bool sent;
|
||||
enum mgcp_verb verb;
|
||||
struct mgcp_conn_peer verb_info;
|
||||
struct osmo_fsm_inst *notify;
|
||||
uint32_t notify_success;
|
||||
uint32_t notify_failure;
|
||||
void *notify_data;
|
||||
struct fsm_notify notify;
|
||||
|
||||
bool got_port_info;
|
||||
struct mgcp_conn_peer rtp_info;
|
||||
@@ -118,6 +123,10 @@ struct osmo_mgcpc_ep {
|
||||
/*! Endpoint connection slots. Note that each connection has its own set of FSM event numbers to signal success
|
||||
* and failure, depending on its index within this array. See CI_EV_SUCCESS and CI_EV_FAILURE. */
|
||||
struct osmo_mgcpc_ep_ci ci[USABLE_CI];
|
||||
|
||||
/*! Internal use: if a function keeps an fsm_notify for later dispatch while already clearing or re-using the
|
||||
* ci[], the fsm_notify should be kept here to also get canceled by osmo_mgcpc_ep_cancel_notify(). */
|
||||
struct llist_head background_notify;
|
||||
};
|
||||
|
||||
const struct value_string osmo_mgcp_verb_names[] = {
|
||||
@@ -246,6 +255,9 @@ struct osmo_mgcpc_ep *osmo_mgcpc_ep_fi_mgwep(struct osmo_fsm_inst *fi)
|
||||
* MGCP messages to set up the endpoint will be sent on the given mgcp_client, as soon as the first
|
||||
* osmo_mgcpc_ep_ci_request() is invoked.
|
||||
*
|
||||
* IMPORTANT: To avoid use-after-free problems, using this FSM requires use of deferred FSM deallocation using
|
||||
* osmo_fsm_set_dealloc_ctx(), e.g. using osmo_select_main_ctx(OTC_SELECT) with osmo_select_main_ctx() as main loop.
|
||||
*
|
||||
* A typical sequence of events would be:
|
||||
*
|
||||
* ep = osmo_mgcpc_ep_alloc(..., mgcp_client_rtpbridge_wildcard(client));
|
||||
@@ -296,6 +308,7 @@ struct osmo_mgcpc_ep *osmo_mgcpc_ep_alloc(struct osmo_fsm_inst *parent, uint32_t
|
||||
.fi = fi,
|
||||
.T_defs = T_defs,
|
||||
};
|
||||
INIT_LLIST_HEAD(&ep->background_notify);
|
||||
fi->priv = ep;
|
||||
|
||||
va_start(ap, endpoint_str_fmt);
|
||||
@@ -354,24 +367,53 @@ static bool osmo_mgcpc_ep_fsm_check_state_chg_after_response(struct osmo_fsm_ins
|
||||
|
||||
static void on_failure(struct osmo_mgcpc_ep_ci *ci)
|
||||
{
|
||||
struct osmo_fsm_inst *notify = ci->notify;
|
||||
uint32_t notify_failure = ci->notify_failure;
|
||||
void *notify_data = ci->notify_data;
|
||||
struct osmo_mgcpc_ep *ep = ci->ep;
|
||||
struct fsm_notify notify;
|
||||
int i;
|
||||
|
||||
if (!ci->occupied)
|
||||
return;
|
||||
|
||||
/* When dispatching an event for this CI, the user may decide to trigger the next request for this conn right
|
||||
* away. So we must be ready with a cleared *ci. Store the notify separately and clear before dispatching. */
|
||||
notify = ci->notify;
|
||||
/* Register the planned notification in ep->background_notify so we also catch any osmo_mgcpc_ep_cancel_notify()
|
||||
* that might be triggered between clearing the ci and actually dispatching the event. */
|
||||
llist_add(¬ify.entry, &ep->background_notify);
|
||||
|
||||
*ci = (struct osmo_mgcpc_ep_ci){
|
||||
.ep = ci->ep,
|
||||
};
|
||||
|
||||
/* An MGCP failure typically means the endpoint becomes unusable, cancel all pending request (except DLCX).
|
||||
* Particularly, if two CRCX were scheduled and the first fails, we must no longer dispatch the second CRCX. */
|
||||
for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
|
||||
struct osmo_mgcpc_ep_ci *other_ci = &ep->ci[i];
|
||||
if (other_ci == ci)
|
||||
continue;
|
||||
if (!other_ci->occupied)
|
||||
continue;
|
||||
if (!other_ci->pending)
|
||||
continue;
|
||||
if (other_ci->sent)
|
||||
continue;
|
||||
if (other_ci->verb == MGCP_VERB_DLCX)
|
||||
continue;
|
||||
/* Just clear the pending request, don't fire more events than below. */
|
||||
other_ci->pending = false;
|
||||
}
|
||||
|
||||
/* If this check has terminated the FSM instance, don't fire any more events to prevent use-after-free problems.
|
||||
* The endpoint FSM does dispatch a term event to its parent, and everything should be cleaned like that. */
|
||||
if (!osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi))
|
||||
if (!osmo_mgcpc_ep_fsm_check_state_chg_after_response(ep->fi)) {
|
||||
/* The ep has deallocated, no need to llist_del(¬ify.entry) here. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (notify)
|
||||
osmo_fsm_inst_dispatch(notify, notify_failure, notify_data);
|
||||
if (notify.fi)
|
||||
osmo_fsm_inst_dispatch(notify.fi, notify.failure, notify.data);
|
||||
|
||||
llist_del(¬ify.entry);
|
||||
}
|
||||
|
||||
static int update_endpoint_name(struct osmo_mgcpc_ep_ci *ci, const char *new_endpoint_name)
|
||||
@@ -452,10 +494,10 @@ static void on_success(struct osmo_mgcpc_ep_ci *ci, void *data)
|
||||
LOG_CI(ci, LOGL_DEBUG, "received successful response to %s: RTP=%s%s\n",
|
||||
osmo_mgcp_verb_name(ci->verb),
|
||||
mgcp_conn_peer_name(ci->got_port_info? &ci->rtp_info : NULL),
|
||||
ci->notify ? "" : " (not sending a notification)");
|
||||
ci->notify.fi ? "" : " (not sending a notification)");
|
||||
|
||||
if (ci->notify)
|
||||
osmo_fsm_inst_dispatch(ci->notify, ci->notify_success, ci->notify_data);
|
||||
if (ci->notify.fi)
|
||||
osmo_fsm_inst_dispatch(ci->notify.fi, ci->notify.success, ci->notify.data);
|
||||
|
||||
osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi);
|
||||
}
|
||||
@@ -516,6 +558,11 @@ static const struct osmo_tdef_state_timeout osmo_mgcpc_ep_fsm_timeouts[32] = {
|
||||
((struct osmo_mgcpc_ep*)fi->priv)->T_defs, 5)
|
||||
|
||||
/*! Dispatch an actual CRCX/MDCX/DLCX message for this connection.
|
||||
*
|
||||
* If the 'notify' instance deallocates before it received a notification of event_success or event_failure,
|
||||
* osmo_mgcpc_ep_ci_cancel_notify() or osmo_mgcpc_ep_cancel_notify() must be called. It is not harmful to cancel
|
||||
* notification after an event has been received.
|
||||
*
|
||||
* \param ci Connection identifier as obtained from osmo_mgcpc_ep_ci_add().
|
||||
* \param verb MGCP operation to dispatch.
|
||||
* \param verb_info Parameters for the MGCP operation.
|
||||
@@ -564,16 +611,18 @@ void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci,
|
||||
.occupied = true,
|
||||
/* .pending = true follows below */
|
||||
.verb = verb,
|
||||
.notify = notify,
|
||||
.notify_success = event_success,
|
||||
.notify_failure = event_failure,
|
||||
.notify_data = notify_data,
|
||||
.notify = {
|
||||
.fi = notify,
|
||||
.success = event_success,
|
||||
.failure = event_failure,
|
||||
.data = notify_data,
|
||||
}
|
||||
};
|
||||
osmo_strlcpy(cleared_ci.label, ci->label, sizeof(cleared_ci.label));
|
||||
osmo_strlcpy(cleared_ci.mgcp_ci_str, ci->mgcp_ci_str, sizeof(cleared_ci.mgcp_ci_str));
|
||||
*ci = cleared_ci;
|
||||
|
||||
LOG_CI_VERB(ci, LOGL_DEBUG, "notify=%s\n", osmo_fsm_inst_name(ci->notify));
|
||||
LOG_CI_VERB(ci, LOGL_DEBUG, "notify=%s\n", osmo_fsm_inst_name(ci->notify.fi));
|
||||
|
||||
if (verb_info)
|
||||
ci->verb_info = *verb_info;
|
||||
@@ -631,10 +680,39 @@ dispatch_error:
|
||||
osmo_fsm_inst_dispatch(notify, event_failure, notify_data);
|
||||
}
|
||||
|
||||
/*! No longer notify for any state changes for any conns of this endpoint.
|
||||
* Useful if the notify instance passed to osmo_mgcpc_ep_ci_request() is about to deallocate.
|
||||
* \param ep The endpoint FSM instance.
|
||||
* \param notify Which target to cancel notification for, if NULL cancel all notifications. */
|
||||
void osmo_mgcpc_ep_cancel_notify(struct osmo_mgcpc_ep *ep, struct osmo_fsm_inst *notify)
|
||||
{
|
||||
struct fsm_notify *n;
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
|
||||
struct osmo_mgcpc_ep_ci *ci = &ep->ci[i];
|
||||
if (!notify || ci->notify.fi == notify)
|
||||
ci->notify.fi = NULL;
|
||||
}
|
||||
llist_for_each_entry(n, &ep->background_notify, entry) {
|
||||
if (!notify || n->fi == notify)
|
||||
n->fi = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Return the osmo_mgcpc_ep that this conn belongs to. */
|
||||
struct osmo_mgcpc_ep *osmo_mgcpc_ep_ci_ep(struct osmo_mgcpc_ep_ci *conn)
|
||||
{
|
||||
if (!conn)
|
||||
return NULL;
|
||||
return conn->ep;
|
||||
}
|
||||
|
||||
static int send_verb(struct osmo_mgcpc_ep_ci *ci)
|
||||
{
|
||||
int rc;
|
||||
struct osmo_mgcpc_ep *ep = ci->ep;
|
||||
struct fsm_notify notify;
|
||||
|
||||
if (!ci->occupied || !ci->pending || ci->sent)
|
||||
return 0;
|
||||
@@ -673,11 +751,14 @@ static int send_verb(struct osmo_mgcpc_ep_ci *ci)
|
||||
osmo_mgcp_verb_name(ci->verb), ci->mgcp_ci_str);
|
||||
/* The way this is designed, we actually need to forget all about the ci right away. */
|
||||
mgcp_conn_delete(ci->mgcp_client_fi);
|
||||
if (ci->notify)
|
||||
osmo_fsm_inst_dispatch(ci->notify, ci->notify_success, ci->notify_data);
|
||||
notify = ci->notify;
|
||||
*ci = (struct osmo_mgcpc_ep_ci){
|
||||
.ep = ep,
|
||||
};
|
||||
/* When dispatching an event for this CI, the user may decide to trigger the next request for this conn
|
||||
* right away. So we must be ready with a cleared *ci. */
|
||||
if (notify.fi)
|
||||
osmo_fsm_inst_dispatch(notify.fi, notify.success, notify.data);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -692,6 +773,7 @@ void osmo_mgcpc_ep_clear(struct osmo_mgcpc_ep *ep)
|
||||
{
|
||||
if (!ep)
|
||||
return;
|
||||
osmo_mgcpc_ep_cancel_notify(ep, NULL);
|
||||
osmo_fsm_inst_term(ep->fi, OSMO_FSM_TERM_REGULAR, 0);
|
||||
}
|
||||
|
||||
|
@@ -525,8 +525,10 @@ static void fsm_cleanup_cb(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause ca
|
||||
LOGPFSML(fi, LOGL_ERROR,
|
||||
"MGW/DLCX: abrupt FSM termination with connections still present, sending unconditional DLCX...\n");
|
||||
msg = make_dlcx_msg(mgcp_ctx);
|
||||
OSMO_ASSERT(msg);
|
||||
mgcp_client_tx(mgcp, msg, NULL, NULL);
|
||||
if (!msg)
|
||||
LOGPFSML(fi, LOGL_ERROR, "MGW/DLCX: Error composing DLCX message\n");
|
||||
else
|
||||
mgcp_client_tx(mgcp, msg, NULL, NULL);
|
||||
}
|
||||
|
||||
talloc_free(mgcp_ctx);
|
||||
@@ -604,7 +606,6 @@ struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm
|
||||
uint32_t parent_term_evt, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx;
|
||||
static bool fsm_registered = false;
|
||||
struct osmo_fsm_inst *fi;
|
||||
struct in_addr ip_test;
|
||||
|
||||
@@ -616,12 +617,6 @@ struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm
|
||||
if (conn_peer->port && inet_aton(conn_peer->addr, &ip_test) == 0)
|
||||
return NULL;
|
||||
|
||||
/* Register the fsm description (if not already done) */
|
||||
if (fsm_registered == false) {
|
||||
osmo_fsm_register(&fsm_mgcp_client);
|
||||
fsm_registered = true;
|
||||
}
|
||||
|
||||
/* Allocate and configure a new fsm instance */
|
||||
fi = osmo_fsm_inst_alloc_child(&fsm_mgcp_client, parent_fi, parent_term_evt);
|
||||
OSMO_ASSERT(fi);
|
||||
@@ -706,6 +701,9 @@ void mgcp_conn_delete(struct osmo_fsm_inst *fi)
|
||||
|
||||
OSMO_ASSERT(mgcp_ctx);
|
||||
|
||||
if (fi->proc.terminating)
|
||||
return;
|
||||
|
||||
/* Unlink FSM from parent */
|
||||
osmo_fsm_inst_unlink_parent(fi, NULL);
|
||||
|
||||
@@ -743,3 +741,8 @@ const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info)
|
||||
return "empty";
|
||||
return buf;
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void osmo_mgcp_client_fsm_init()
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&fsm_mgcp_client) == 0);
|
||||
}
|
||||
|
@@ -40,4 +40,6 @@ libosmo_mgcp_a_SOURCES = \
|
||||
mgcp_conn.c \
|
||||
mgcp_stat.c \
|
||||
mgcp_endp.c \
|
||||
iuup_protocol.c \
|
||||
iuup_cn_node.c \
|
||||
$(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. */
|
||||
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)
|
||||
talloc_free(codec->subtype_name);
|
||||
if (codec->audio_name)
|
||||
talloc_free(codec->audio_name);
|
||||
memset(codec, 0, sizeof(*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;
|
||||
*codec = (struct mgcp_rtp_codec){};
|
||||
}
|
||||
|
||||
/*! Initalize or reset codec information with default data.
|
||||
* \param[out] conn related rtp-connection. */
|
||||
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.codec = NULL;
|
||||
}
|
||||
|
||||
/* Set members of struct mgcp_rtp_codec, extrapolate in missing information. Param audio_name is expected in uppercase. */
|
||||
static int codec_set(void *ctx, struct mgcp_rtp_codec *codec, int payload_type, const char *audio_name,
|
||||
unsigned int pt_offset, const struct mgcp_codec_param *param)
|
||||
/*! 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 rate;
|
||||
int channels;
|
||||
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 */
|
||||
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) {
|
||||
/* Make sure we do not get any reserved or undefined type numbers */
|
||||
/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
|
||||
if (payload_type == 1 || payload_type == 2 || payload_type == 19)
|
||||
goto error;
|
||||
if (payload_type >= 72 && payload_type <= 76)
|
||||
goto error;
|
||||
if (payload_type >= 127)
|
||||
if ((payload_type == 1 || payload_type == 2 || payload_type == 19)
|
||||
|| (payload_type >= 72 && payload_type <= 76)
|
||||
|| (payload_type >= 127)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Cannot add codec, payload type number %d is reserved\n",
|
||||
payload_type);
|
||||
goto error;
|
||||
}
|
||||
|
||||
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
|
||||
* it is a dynamic payload type for which we do not
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
* two are optional. If they are not present we use the safe defaults
|
||||
* 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;
|
||||
}
|
||||
channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
codec->rate = rate;
|
||||
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 */
|
||||
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
|
||||
* 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
|
||||
* also fixed, see above) */
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,41 +267,14 @@ static int codec_set(void *ctx, struct mgcp_rtp_codec *codec, int payload_type,
|
||||
} else
|
||||
codec->param_present = false;
|
||||
|
||||
conn->end.codecs_assigned++;
|
||||
return 0;
|
||||
error:
|
||||
/* Make sure we leave a clean codec entry on error. */
|
||||
codec_init(codec);
|
||||
memset(codec, 0, sizeof(*codec));
|
||||
codec_free(codec);
|
||||
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
|
||||
* Helper function for mgcp_codec_decide() */
|
||||
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 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
|
||||
* number. */
|
||||
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;
|
||||
if (codec_a->frame_duration_den != codec_b->frame_duration_den)
|
||||
return false;
|
||||
if (strcmp(codec_a->audio_name, codec_b->audio_name))
|
||||
return false;
|
||||
if (strcmp(codec_a->subtype_name, codec_b->subtype_name))
|
||||
return false;
|
||||
if (!strcmp(codec_a->subtype_name, "AMR")) {
|
||||
if (amr_is_octet_aligned(codec_a) != amr_is_octet_aligned(codec_b))
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
mgcp_free_rtp_port(&conn_rtp->end);
|
||||
rate_ctr_group_free(conn_rtp->rate_ctr_group);
|
||||
mgcp_codec_reset_all(conn_rtp);
|
||||
}
|
||||
|
||||
void mgcp_conn_watchdog_cb(void *data)
|
||||
|
@@ -45,6 +45,8 @@
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <osmocom/mgcp/debug.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)
|
||||
@@ -52,11 +54,13 @@
|
||||
#define RTP_MAX_MISORDER 100
|
||||
#define RTP_BUF_SIZE 4096
|
||||
|
||||
enum {
|
||||
enum rtp_proto {
|
||||
MGCP_PROTO_RTP,
|
||||
MGCP_PROTO_RTCP,
|
||||
};
|
||||
|
||||
static int rx_rtp(struct msgb *msg);
|
||||
|
||||
/*! Determine the local rtp bind 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
|
||||
@@ -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
|
||||
* that is valid for the destination connection (conn_dst) */
|
||||
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;
|
||||
uint8_t pt_in;
|
||||
int pt_out;
|
||||
|
||||
OSMO_ASSERT(len >= sizeof(struct rtp_hdr));
|
||||
rtp_hdr = (struct rtp_hdr *)data;
|
||||
|
||||
pt_in = rtp_hdr->payload_type;
|
||||
pt_out = mgcp_codec_pt_translate(conn_src, conn_dst, pt_in);
|
||||
if (pt_out < 0)
|
||||
if (msg->len < sizeof(struct rtp_hdr)) {
|
||||
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTP packet too short (%u < %zu)\n",
|
||||
msg->len, sizeof(struct rtp_hdr));
|
||||
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;
|
||||
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,
|
||||
struct mgcp_rtp_state *state,
|
||||
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;
|
||||
int32_t transit;
|
||||
@@ -504,11 +532,12 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
uint32_t timestamp, ssrc;
|
||||
struct rtp_hdr *rtp_hdr;
|
||||
int payload = rtp_end->codec->payload_type;
|
||||
unsigned int len = msg->len;
|
||||
|
||||
if (len < sizeof(*rtp_hdr))
|
||||
return;
|
||||
|
||||
rtp_hdr = (struct rtp_hdr *)data;
|
||||
rtp_hdr = (struct rtp_hdr *)msg->data;
|
||||
seq = ntohs(rtp_hdr->sequence);
|
||||
timestamp = ntohl(rtp_hdr->timestamp);
|
||||
arrival_time = get_current_ts(rtp_end->codec->rate);
|
||||
@@ -640,8 +669,9 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
/* There are different dialects used to format and transfer voice data. When
|
||||
* 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
|
||||
* use. */
|
||||
static void rfc5993_hr_convert(struct mgcp_endpoint *endp, char *data, int *len)
|
||||
* use.
|
||||
* Return 0 on sucess, negative on errors like invalid data length. */
|
||||
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
|
||||
* plenty of space available to store the slightly larger, converted
|
||||
@@ -649,7 +679,12 @@ static void rfc5993_hr_convert(struct mgcp_endpoint *endp, char *data, int *len)
|
||||
|
||||
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;
|
||||
|
||||
if (*len == GSM_HR_BYTES + sizeof(struct rtp_hdr)) {
|
||||
@@ -667,7 +702,9 @@ static void rfc5993_hr_convert(struct mgcp_endpoint *endp, char *data, int *len)
|
||||
* packet. This is not supported yet. */
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"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
|
||||
@@ -685,7 +722,11 @@ static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, char *data, int *len,
|
||||
unsigned int payload_len;
|
||||
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;
|
||||
|
||||
payload_len = *len - sizeof(struct rtp_hdr);
|
||||
@@ -735,31 +776,35 @@ static bool amr_oa_bwe_convert_indicated(struct mgcp_rtp_codec *codec)
|
||||
}
|
||||
|
||||
|
||||
/* Check if a given RTP with AMR payload for octet-aligned mode */
|
||||
static bool amr_oa_check(char *data, int len)
|
||||
/* Return whether an RTP packet with AMR payload is in octet-aligned mode.
|
||||
* Return 0 if in bandwidth-efficient mode, 1 for octet-aligned mode, and negative if the RTP data is invalid. */
|
||||
static int amr_oa_check(char *data, int len)
|
||||
{
|
||||
struct rtp_hdr *rtp_hdr;
|
||||
unsigned int payload_len;
|
||||
|
||||
OSMO_ASSERT(len >= sizeof(struct rtp_hdr));
|
||||
if (len < sizeof(struct rtp_hdr))
|
||||
return -EINVAL;
|
||||
|
||||
rtp_hdr = (struct rtp_hdr *)data;
|
||||
|
||||
payload_len = len - sizeof(struct rtp_hdr);
|
||||
if (payload_len < sizeof(struct amr_hdr))
|
||||
return -EINVAL;
|
||||
|
||||
return osmo_amr_is_oa(rtp_hdr->data, payload_len);
|
||||
return osmo_amr_is_oa(rtp_hdr->data, payload_len) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* Forward data to a debug tap. This is debug function that is intended for
|
||||
* debugging the voice traffic with tools like gstreamer */
|
||||
static void forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf,
|
||||
int len)
|
||||
static void forward_data(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!tap->enabled)
|
||||
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));
|
||||
|
||||
if (rc < 0)
|
||||
@@ -777,7 +822,7 @@ static void forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf,
|
||||
* \param[in] conn_dst associated destination connection
|
||||
* \returns 0 on success, -1 on ERROR */
|
||||
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)
|
||||
{
|
||||
/*! When no destination connection is available (e.g. when only one
|
||||
@@ -789,6 +834,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
struct mgcp_rtp_state *rtp_state;
|
||||
char *dest_name;
|
||||
int rc;
|
||||
int len;
|
||||
|
||||
OSMO_ASSERT(conn_src);
|
||||
OSMO_ASSERT(conn_dst);
|
||||
@@ -812,7 +858,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
* should not occur if transcoding is consequently avoided. Until
|
||||
* we have transcoding support in osmo-mgw we can not resolve this. */
|
||||
if (is_rtp) {
|
||||
rc = mgcp_patch_pt(conn_src, conn_dst, buf, len);
|
||||
rc = mgcp_patch_pt(conn_src, conn_dst, msg);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"can not patch PT because no suitable egress codec was found.\n");
|
||||
@@ -837,27 +883,37 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
} else if (is_rtp) {
|
||||
int cont;
|
||||
int nbytes = 0;
|
||||
int buflen = len;
|
||||
int buflen = msg->len;
|
||||
do {
|
||||
/* Run transcoder */
|
||||
cont = endp->cfg->rtp_processing_cb(endp, rtp_end,
|
||||
buf, &buflen,
|
||||
(char*)msg->data, &buflen,
|
||||
RTP_BUF_SIZE);
|
||||
if (cont < 0)
|
||||
break;
|
||||
|
||||
if (addr)
|
||||
mgcp_patch_and_count(endp, rtp_state, rtp_end,
|
||||
addr, buf, buflen);
|
||||
addr, msg);
|
||||
|
||||
if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) {
|
||||
amr_oa_bwe_convert(endp, buf, &buflen,
|
||||
conn_dst->end.codec->param.amr_octet_aligned);
|
||||
rc = amr_oa_bwe_convert(endp, (char*)msg->data, &buflen,
|
||||
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
|
||||
&& strcmp(conn_src->end.codec->subtype_name,
|
||||
"GSM-HR-08") == 0)
|
||||
rfc5993_hr_convert(endp, buf, &buflen);
|
||||
"GSM-HR-08") == 0) {
|
||||
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,
|
||||
"process/send to %s %s "
|
||||
@@ -868,8 +924,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_data(rtp_end->rtp.fd, &conn_src->tap_out,
|
||||
buf, buflen);
|
||||
msg);
|
||||
|
||||
#if 0
|
||||
/* FIXME: HACK HACK HACK. See OS#2459.
|
||||
* The ip.access nano3G needs the first RTP payload's first two bytes to read hex
|
||||
* 'e400', or it will reject the RAB assignment. It seems to not harm other femto
|
||||
@@ -877,7 +934,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
*/
|
||||
if (!rtp_state->patched_first_rtp_payload
|
||||
&& conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
|
||||
uint8_t *data = (uint8_t *) & buf[12];
|
||||
uint8_t *data = msg->data + 12;
|
||||
if (data[0] == 0xe0) {
|
||||
data[0] = 0xe4;
|
||||
data[1] = 0x00;
|
||||
@@ -887,10 +944,13 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
" to fake an IuUP Initialization Ack\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
len = mgcp_udp_send(rtp_end->rtp.fd,
|
||||
&rtp_end->addr,
|
||||
rtp_end->rtp_port, buf, buflen);
|
||||
if (conn_dst->iuup)
|
||||
len = osmo_iuup_cn_tx_payload(conn_dst->iuup, msg);
|
||||
else
|
||||
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port,
|
||||
(char*)msg->data, msg->len);
|
||||
|
||||
if (len <= 0)
|
||||
return len;
|
||||
@@ -911,7 +971,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
|
||||
len = mgcp_udp_send(rtp_end->rtcp.fd,
|
||||
&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_add(&conn_dst->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], len);
|
||||
@@ -922,45 +982,6 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
|
||||
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
|
||||
* connections. */
|
||||
static int check_rtp_origin(struct mgcp_conn_rtp *conn,
|
||||
@@ -1053,7 +1074,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
|
||||
* 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;
|
||||
unsigned int len;
|
||||
@@ -1061,33 +1082,45 @@ static int check_rtcp(char *buf, unsigned int buf_size)
|
||||
|
||||
/* RTPC packets that are just a header without data do not make
|
||||
* 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;
|
||||
}
|
||||
|
||||
/* Make sure that the length of the received packet does not exceed
|
||||
* the available buffer size */
|
||||
hdr = (struct rtcp_hdr *)buf;
|
||||
hdr = (struct rtcp_hdr *)msg->data;
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
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 0;
|
||||
}
|
||||
|
||||
/* Do some basic checks to make sure that the RTP packets we are going to
|
||||
* 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
|
||||
* any sense. */
|
||||
if (buf_size < sizeof(struct rtp_hdr))
|
||||
return -EINVAL;
|
||||
size_t min_size = sizeof(struct rtp_hdr);
|
||||
if (conn_src->iuup)
|
||||
min_size += sizeof(struct osmo_iuup_hdr_data);
|
||||
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
|
||||
* the length is because we currently handle IUUP packets as RTP
|
||||
@@ -1098,86 +1131,14 @@ static int check_rtp(char *buf, unsigned int buf_size)
|
||||
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
|
||||
* transmission or trsmission via an osmux connection */
|
||||
static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
|
||||
unsigned int buf_size,
|
||||
struct mgcp_conn_rtp *conn_src,
|
||||
struct mgcp_conn_rtp *conn_dst)
|
||||
static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
|
||||
{
|
||||
struct mgcp_endpoint *endp;
|
||||
endp = conn_src->conn->endp;
|
||||
enum rtp_proto proto = OSMO_RTP_MSG_CTX(msg)->proto;
|
||||
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",
|
||||
mgcp_conn_dump(conn_dst->conn));
|
||||
@@ -1196,13 +1157,13 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
|
||||
"endpoint type is MGCP_RTP_DEFAULT, "
|
||||
"using mgcp_send() to forward data directly\n");
|
||||
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:
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG,
|
||||
"endpoint type is MGCP_OSMUX_BSC_NAT, "
|
||||
"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
|
||||
@@ -1220,10 +1181,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] conn originating connection
|
||||
* \returns 0 on success, -1 on ERROR */
|
||||
int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
|
||||
unsigned int buf_size, struct mgcp_conn *conn)
|
||||
int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
|
||||
{
|
||||
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 sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
|
||||
|
||||
/*! NOTE: This callback function implements the endpoint specific
|
||||
* dispatch bahviour of an rtp bridge/proxy endpoint. It is assumed
|
||||
@@ -1242,11 +1205,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
|
||||
* outgoing address in connection on the fly */
|
||||
if (conn->u.rtp.end.rtp_port == 0) {
|
||||
conn->u.rtp.end.addr = addr->sin_addr;
|
||||
conn->u.rtp.end.rtp_port = addr->sin_port;
|
||||
conn->u.rtp.end.addr = from_addr->sin_addr;
|
||||
conn->u.rtp.end.rtp_port = from_addr->sin_port;
|
||||
}
|
||||
return mgcp_send_rtp(proto, addr, buf,
|
||||
buf_size, &conn->u.rtp, &conn->u.rtp);
|
||||
return mgcp_send_rtp(conn_src, msg);
|
||||
}
|
||||
|
||||
/* Find a destination connection. */
|
||||
@@ -1278,9 +1240,7 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
|
||||
}
|
||||
|
||||
/* Dispatch RTP packet to destination RTP connection */
|
||||
return mgcp_send_rtp(proto, addr, buf,
|
||||
buf_size, &conn->u.rtp, &conn_dst->u.rtp);
|
||||
|
||||
return mgcp_send_rtp(&conn_dst->u.rtp, msg);
|
||||
}
|
||||
|
||||
/*! cleanup an endpoint when a connection on an RTP bridge endpoint is removed.
|
||||
@@ -1302,6 +1262,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 */
|
||||
static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
{
|
||||
@@ -1315,23 +1345,88 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
struct mgcp_conn_rtp *conn_src;
|
||||
struct mgcp_endpoint *endp;
|
||||
struct sockaddr_in addr;
|
||||
|
||||
char buf[RTP_BUF_SIZE];
|
||||
int proto;
|
||||
int len;
|
||||
socklen_t slen = sizeof(addr);
|
||||
int ret;
|
||||
enum rtp_proto proto;
|
||||
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;
|
||||
OSMO_ASSERT(conn_src);
|
||||
endp = conn_src->conn->endp;
|
||||
OSMO_ASSERT(endp);
|
||||
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "source conn:%s\n",
|
||||
mgcp_conn_dump(conn_src->conn));
|
||||
proto = (fd == &conn_src->end.rtp)? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
|
||||
|
||||
/* Receive packet */
|
||||
len = mgcp_recv(&proto, &addr, buf, sizeof(buf), fd);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
ret = recvfrom(fd->fd, msg->data, msg->data_len, 0, (struct sockaddr *)&addr, &slen);
|
||||
|
||||
if (ret <= 0) {
|
||||
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);
|
||||
|
||||
@@ -1340,14 +1435,20 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
|
||||
* define, then we check if the incoming payload matches that
|
||||
* expectation. */
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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
|
||||
* dispatching of the RTP data */
|
||||
return endp->type->dispatch_rtp_cb(proto, &addr, buf, len,
|
||||
conn_src->conn);
|
||||
return conn->endp->type->dispatch_rtp_cb(msg);
|
||||
}
|
||||
|
||||
/*! 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_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(MGCP_PROTO_RTP, &addr, (char *)msg->data, msg->len, conn->conn);
|
||||
endp->type->dispatch_rtp_cb(msg);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
|
@@ -919,7 +919,7 @@ mgcp_header_done:
|
||||
endp->callid = talloc_strdup(tcfg->endpoints, callid);
|
||||
|
||||
snprintf(conn_name, sizeof(conn_name), "%s", callid);
|
||||
_conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP, conn_name);
|
||||
_conn = mgcp_conn_alloc(tcfg->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
|
||||
if (!_conn) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
"CRCX: unable to allocate RTP connection\n");
|
||||
|
@@ -132,6 +132,10 @@ static void handle_options(int argc, char **argv)
|
||||
break;
|
||||
};
|
||||
}
|
||||
if (argc > optind) {
|
||||
fprintf(stderr, "Unsupported positional arguments on command line\n");
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
/* Callback function to be called when the RSIP ("Reset in Progress") mgcp
|
||||
@@ -244,6 +248,12 @@ static const struct log_info_cat log_categories[] = {
|
||||
.color = "\033[1;30m",
|
||||
.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 = {
|
||||
|
@@ -1,6 +1,7 @@
|
||||
SUBDIRS = \
|
||||
mgcp_client \
|
||||
mgcp \
|
||||
iuup \
|
||||
$(NULL)
|
||||
|
||||
# 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_sdp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
@@ -641,6 +642,13 @@ int clock_gettime(clockid_t clk_id, struct timespec *tp)
|
||||
return real_clock_gettime(clk_id, tp);
|
||||
}
|
||||
|
||||
static void mgcp_endpoints_release(struct mgcp_trunk_config *trunk)
|
||||
{
|
||||
int i;
|
||||
for (i = 1; i < trunk->number_endpoints; i++)
|
||||
mgcp_endp_release(&trunk->endpoints[i]);
|
||||
}
|
||||
|
||||
#define CONN_UNMODIFIED (0x1000)
|
||||
|
||||
static void test_values(void)
|
||||
@@ -742,6 +750,7 @@ static void test_messages(void)
|
||||
{
|
||||
struct mgcp_config *cfg;
|
||||
struct mgcp_endpoint *endp;
|
||||
struct mgcp_trunk_config *trunk2;
|
||||
int i;
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
char last_conn_id[256];
|
||||
@@ -755,7 +764,8 @@ static void test_messages(void)
|
||||
|
||||
memset(last_conn_id, 0, sizeof(last_conn_id));
|
||||
|
||||
mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1));
|
||||
trunk2 = mgcp_trunk_alloc(cfg, 1);
|
||||
mgcp_endpoints_allocate(trunk2);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
const struct mgcp_test *t = &tests[i];
|
||||
@@ -873,12 +883,15 @@ static void test_messages(void)
|
||||
}
|
||||
}
|
||||
|
||||
mgcp_endpoints_release(trunk2);
|
||||
mgcp_endpoints_release(&cfg->trunk);
|
||||
talloc_free(cfg);
|
||||
}
|
||||
|
||||
static void test_retransmission(void)
|
||||
{
|
||||
struct mgcp_config *cfg;
|
||||
struct mgcp_trunk_config *trunk2;
|
||||
int i;
|
||||
char last_conn_id[256];
|
||||
int rc;
|
||||
@@ -890,7 +903,8 @@ static void test_retransmission(void)
|
||||
|
||||
memset(last_conn_id, 0, sizeof(last_conn_id));
|
||||
|
||||
mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1));
|
||||
trunk2 = mgcp_trunk_alloc(cfg, 1);
|
||||
mgcp_endpoints_allocate(trunk2);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(retransmit); i++) {
|
||||
const struct mgcp_test *t = &retransmit[i];
|
||||
@@ -930,6 +944,8 @@ static void test_retransmission(void)
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
mgcp_endpoints_release(trunk2);
|
||||
mgcp_endpoints_release(&cfg->trunk);
|
||||
talloc_free(cfg);
|
||||
}
|
||||
|
||||
@@ -943,6 +959,7 @@ static int rqnt_cb(struct mgcp_endpoint *endp, char _tone)
|
||||
static void test_rqnt_cb(void)
|
||||
{
|
||||
struct mgcp_config *cfg;
|
||||
struct mgcp_trunk_config *trunk2;
|
||||
struct msgb *inp, *msg;
|
||||
char conn_id[256];
|
||||
|
||||
@@ -952,7 +969,8 @@ static void test_rqnt_cb(void)
|
||||
cfg->trunk.vty_number_endpoints = 64;
|
||||
mgcp_endpoints_allocate(&cfg->trunk);
|
||||
|
||||
mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1));
|
||||
trunk2 = mgcp_trunk_alloc(cfg, 1);
|
||||
mgcp_endpoints_allocate(trunk2);
|
||||
|
||||
inp = create_msg(CRCX, NULL);
|
||||
msg = mgcp_handle_message(cfg, inp);
|
||||
@@ -981,6 +999,8 @@ static void test_rqnt_cb(void)
|
||||
inp = create_msg(DLCX, conn_id);
|
||||
msgb_free(mgcp_handle_message(cfg, inp));
|
||||
msgb_free(inp);
|
||||
mgcp_endpoints_release(trunk2);
|
||||
mgcp_endpoints_release(&cfg->trunk);
|
||||
talloc_free(cfg);
|
||||
}
|
||||
|
||||
@@ -1237,7 +1257,7 @@ struct rtp_packet_info test_rtp_packets1[] = {
|
||||
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_state *state,
|
||||
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)
|
||||
{
|
||||
@@ -1249,7 +1269,6 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
struct mgcp_rtp_state state;
|
||||
struct mgcp_rtp_end *rtp;
|
||||
struct sockaddr_in addr = { 0 };
|
||||
char buffer[4096];
|
||||
uint32_t last_ssrc = 0;
|
||||
uint32_t last_timestamp = 0;
|
||||
uint32_t last_seqno = 0;
|
||||
@@ -1297,16 +1316,17 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(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);
|
||||
|
||||
OSMO_ASSERT(info->len <= sizeof(buffer));
|
||||
OSMO_ASSERT(info->len <= msgb_tailroom(msg));
|
||||
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_patch_and_count(&endp, &state, rtp, &addr,
|
||||
buffer, info->len);
|
||||
mgcp_patch_and_count(&endp, &state, rtp, &addr, msg);
|
||||
|
||||
if (state.out_stream.ssrc != last_ssrc) {
|
||||
printf("Output SSRC changed to %08x\n",
|
||||
@@ -1333,6 +1353,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_timestamp = state.out_stream.last_timestamp;
|
||||
last_seqno = state.out_stream.last_seq;
|
||||
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
force_monotonic_time_us = -1;
|
||||
@@ -1342,12 +1364,12 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
static void test_multilple_codec(void)
|
||||
{
|
||||
struct mgcp_config *cfg;
|
||||
struct mgcp_trunk_config *trunk2;
|
||||
struct mgcp_endpoint *endp;
|
||||
struct msgb *inp, *resp;
|
||||
struct in_addr addr;
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
char conn_id[256];
|
||||
int i;
|
||||
|
||||
printf("Testing multiple payload types\n");
|
||||
|
||||
@@ -1355,7 +1377,9 @@ static void test_multilple_codec(void)
|
||||
cfg->trunk.vty_number_endpoints = 64;
|
||||
mgcp_endpoints_allocate(&cfg->trunk);
|
||||
cfg->policy_cb = mgcp_test_policy_cb;
|
||||
mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1));
|
||||
|
||||
trunk2 = mgcp_trunk_alloc(cfg, 1);
|
||||
mgcp_endpoints_allocate(trunk2);
|
||||
|
||||
/* Allocate endpoint 1@mgw with two codecs */
|
||||
last_endpoint = -1;
|
||||
@@ -1481,9 +1505,8 @@ static void test_multilple_codec(void)
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 0);
|
||||
|
||||
for (i = 1; i < cfg->trunk.number_endpoints; i++)
|
||||
mgcp_endp_release(&cfg->trunk.endpoints[i]);
|
||||
|
||||
mgcp_endpoints_release(trunk2);
|
||||
mgcp_endpoints_release(&cfg->trunk);
|
||||
talloc_free(cfg);
|
||||
}
|
||||
|
||||
@@ -1532,12 +1555,13 @@ static void test_no_cycle(void)
|
||||
OSMO_ASSERT(conn->state.stats.cycles == UINT16_MAX + 1);
|
||||
OSMO_ASSERT(conn->state.stats.max_seq == 0);
|
||||
|
||||
mgcp_endp_release(endp);
|
||||
mgcp_endpoints_release(&cfg->trunk);
|
||||
talloc_free(cfg);
|
||||
}
|
||||
|
||||
static void test_no_name(void)
|
||||
{
|
||||
struct mgcp_trunk_config *trunk2;
|
||||
struct mgcp_config *cfg;
|
||||
struct msgb *inp, *msg;
|
||||
|
||||
@@ -1550,7 +1574,8 @@ static void test_no_name(void)
|
||||
|
||||
cfg->policy_cb = mgcp_test_policy_cb;
|
||||
|
||||
mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1));
|
||||
trunk2 = mgcp_trunk_alloc(cfg, 1);
|
||||
mgcp_endpoints_allocate(trunk2);
|
||||
|
||||
inp = create_msg(CRCX, NULL);
|
||||
msg = mgcp_handle_message(cfg, inp);
|
||||
@@ -1563,7 +1588,8 @@ static void test_no_name(void)
|
||||
msgb_free(inp);
|
||||
msgb_free(msg);
|
||||
|
||||
mgcp_endp_release(&cfg->trunk.endpoints[1]);
|
||||
mgcp_endpoints_release(trunk2);
|
||||
mgcp_endpoints_release(&cfg->trunk);
|
||||
talloc_free(cfg);
|
||||
}
|
||||
|
||||
@@ -1711,98 +1737,335 @@ static void test_check_local_cx_options(void *ctx)
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, ",,,") == -1);
|
||||
}
|
||||
|
||||
static void test_mgcp_codec_pt_translate_pars(struct mgcp_rtp_codec *c)
|
||||
{
|
||||
c->rate = 8000;
|
||||
c->channels = 1;
|
||||
c->frame_duration_num = 23;
|
||||
c->frame_duration_den = 42;
|
||||
}
|
||||
static const struct mgcp_codec_param amr_param_octet_aligned_true = {
|
||||
.amr_octet_aligned_present = true,
|
||||
.amr_octet_aligned = true,
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
struct mgcp_conn_rtp conn_src;
|
||||
struct mgcp_conn_rtp conn_dst;
|
||||
int pt_dst;
|
||||
int i;
|
||||
bool ok = true;
|
||||
printf("\nTesting mgcp_codec_pt_translate()\n");
|
||||
|
||||
/* Setup a realistic set of codec configurations on both
|
||||
* ends. AMR and HR will use different payload types. PCMU
|
||||
* must use 0 on both ends since this is not a dynamic payload
|
||||
* type */
|
||||
test_mgcp_codec_pt_translate_pars(&conn_src.end.codecs[0]);
|
||||
test_mgcp_codec_pt_translate_pars(&conn_dst.end.codecs[0]);
|
||||
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;
|
||||
for (i = 0; i < ARRAY_SIZE(test_mgcp_codec_pt_translate_cases); i++) {
|
||||
const struct testcase_mgcp_codec_pt_translate *t = &test_mgcp_codec_pt_translate_cases[i];
|
||||
struct mgcp_conn_rtp conn[2] = {};
|
||||
int rc;
|
||||
int conn_i;
|
||||
int c;
|
||||
|
||||
/* We expect the function to find the PT we must use when we send the
|
||||
* 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);
|
||||
printf("#%d: %s\n", i, t->descr);
|
||||
|
||||
/* Try some constellations that must fail */
|
||||
pt_dst = mgcp_codec_pt_translate(&conn_src, &conn_dst, 123);
|
||||
OSMO_ASSERT(pt_dst == -EINVAL);
|
||||
conn_src.end.codecs_assigned = 0;
|
||||
conn_dst.end.codecs_assigned = 3;
|
||||
pt_dst =
|
||||
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
||||
conn_src.end.codecs[0].payload_type);
|
||||
OSMO_ASSERT(pt_dst == -EINVAL);
|
||||
pt_dst =
|
||||
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
||||
conn_src.end.codecs[1].payload_type);
|
||||
OSMO_ASSERT(pt_dst == -EINVAL);
|
||||
pt_dst =
|
||||
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
||||
conn_src.end.codecs[2].payload_type);
|
||||
OSMO_ASSERT(pt_dst == -EINVAL);
|
||||
conn_src.end.codecs_assigned = 3;
|
||||
conn_dst.end.codecs_assigned = 0;
|
||||
pt_dst =
|
||||
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
||||
conn_src.end.codecs[0].payload_type);
|
||||
OSMO_ASSERT(pt_dst == -EINVAL);
|
||||
pt_dst =
|
||||
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
||||
conn_src.end.codecs[1].payload_type);
|
||||
OSMO_ASSERT(pt_dst == -EINVAL);
|
||||
pt_dst =
|
||||
mgcp_codec_pt_translate(&conn_src, &conn_dst,
|
||||
conn_src.end.codecs[2].payload_type);
|
||||
OSMO_ASSERT(pt_dst == -EINVAL);
|
||||
for (conn_i = 0; conn_i < 2; conn_i++) {
|
||||
printf(" - add codecs on conn%d:\n", conn_i);
|
||||
for (c = 0; c < ARRAY_SIZE(t->codecs[conn_i]); c++) {
|
||||
const struct testcase_mgcp_codec_pt_translate_codec *codec = &t->codecs[conn_i][c];
|
||||
if (!codec->audio_name)
|
||||
break;
|
||||
|
||||
rc = mgcp_codec_add(&conn[conn_i], codec->payload_type, codec->audio_name, codec->param);
|
||||
|
||||
printf(" %2d: %3d %s%s -> rc=%d\n", c, codec->payload_type, codec->audio_name,
|
||||
codec->param ?
|
||||
(codec->param->amr_octet_aligned_present?
|
||||
(codec->param->amr_octet_aligned ?
|
||||
" octet-aligned=1" : " octet-aligned=0")
|
||||
: " octet-aligned=unset")
|
||||
: "",
|
||||
rc);
|
||||
if (rc != codec->expect_rc) {
|
||||
printf(" ERROR: expected rc=%d\n", codec->expect_rc);
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
if (!c)
|
||||
printf(" (none)\n");
|
||||
}
|
||||
|
||||
for (c = 0; c < ARRAY_SIZE(t->expect); c++) {
|
||||
const struct testcase_mgcp_codec_pt_translate_expect *expect = &t->expect[c];
|
||||
int result;
|
||||
|
||||
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()
|
||||
|
@@ -1218,6 +1218,148 @@ p:10, a:PCMU -> p:10, a:PCMU
|
||||
Testing get_lco_identifier()
|
||||
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:
|
||||
0: 111 GSM-HR-08/8000 -> rc=0
|
||||
1: 112 GSM-HR-08/8000/2 -> rc=-22
|
||||
|
@@ -360,7 +360,7 @@ struct sdp_section_start_test {
|
||||
static struct sdp_section_start_test sdp_section_start_tests[] = {
|
||||
{
|
||||
.body = "",
|
||||
.expect_rc = -EINVAL,
|
||||
.expect_rc = 0,
|
||||
},
|
||||
{
|
||||
.body = "\n\n",
|
||||
@@ -399,19 +399,19 @@ static struct sdp_section_start_test sdp_section_start_tests[] = {
|
||||
.body = "some mgcp header data\r\nand header params"
|
||||
"\n\r\n"
|
||||
"m=audio 23\r\n",
|
||||
.expect_rc = -EINVAL,
|
||||
.expect_rc = 0,
|
||||
},
|
||||
{
|
||||
.body = "some mgcp header data\r\nand header params"
|
||||
"\r\n\r"
|
||||
"m=audio 23\r\n",
|
||||
.expect_rc = -EINVAL,
|
||||
.expect_rc = 0,
|
||||
},
|
||||
{
|
||||
.body = "some mgcp header data\r\nand header params"
|
||||
"\n\r\r"
|
||||
"m=audio 23\r\n",
|
||||
.expect_rc = -EINVAL,
|
||||
.expect_rc = 0,
|
||||
},
|
||||
};
|
||||
|
||||
|
@@ -17,8 +17,9 @@ test_mgcp_client_cancel() done
|
||||
|
||||
test_sdp_section_start() test [0]:
|
||||
body: ""
|
||||
DLMGCP MGCP response: cannot find start of SDP parameters
|
||||
got rc=-22
|
||||
DLMGCP MGCP response contains no SDP parameters
|
||||
got rc=0
|
||||
got audio_port=0
|
||||
|
||||
test_sdp_section_start() test [1]:
|
||||
body: "\n\n"
|
||||
@@ -52,18 +53,21 @@ got audio_port=23
|
||||
|
||||
test_sdp_section_start() test [7]:
|
||||
body: "some mgcp header data\r\nand header params\n\r\nm=audio 23\r\n"
|
||||
DLMGCP MGCP response: cannot find start of SDP parameters
|
||||
got rc=-22
|
||||
DLMGCP MGCP response contains no SDP parameters
|
||||
got rc=0
|
||||
got audio_port=0
|
||||
|
||||
test_sdp_section_start() test [8]:
|
||||
body: "some mgcp header data\r\nand header params\r\n\rm=audio 23\r\n"
|
||||
DLMGCP MGCP response: cannot find start of SDP parameters
|
||||
got rc=-22
|
||||
DLMGCP MGCP response contains no SDP parameters
|
||||
got rc=0
|
||||
got audio_port=0
|
||||
|
||||
test_sdp_section_start() test [9]:
|
||||
body: "some mgcp header data\r\nand header params\n\r\rm=audio 23\r\n"
|
||||
DLMGCP MGCP response: cannot find start of SDP parameters
|
||||
got rc=-22
|
||||
DLMGCP MGCP response contains no SDP parameters
|
||||
got rc=0
|
||||
got audio_port=0
|
||||
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
|
||||
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
|
||||
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
|
||||
|
@@ -13,3 +13,10 @@ AT_KEYWORDS([mgcp])
|
||||
cat $abs_srcdir/mgcp/mgcp_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore])
|
||||
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