summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--configure.ac8
-rwxr-xr-xgit-version-gen4
-rw-r--r--include/osmocom/legacy_mgcp/mgcp.h6
-rw-r--r--include/osmocom/legacy_mgcp/mgcp_internal.h14
-rw-r--r--include/osmocom/mgcp/Makefile.am1
-rw-r--r--include/osmocom/mgcp/mgcp.h8
-rw-r--r--include/osmocom/mgcp/mgcp_codec.h6
-rw-r--r--include/osmocom/mgcp/mgcp_common.h4
-rw-r--r--include/osmocom/mgcp/mgcp_conn.h11
-rw-r--r--include/osmocom/mgcp/mgcp_endp.h24
-rw-r--r--include/osmocom/mgcp/mgcp_internal.h52
-rw-r--r--include/osmocom/mgcp/mgcp_sdp.h4
-rw-r--r--include/osmocom/mgcp/mgcp_stat.h3
-rw-r--r--include/osmocom/mgcp_client/mgcp_client.h43
-rw-r--r--include/osmocom/mgcp_client/mgcp_client_fsm.h19
-rw-r--r--src/libosmo-legacy-mgcp/Makefile.am2
-rw-r--r--src/libosmo-legacy-mgcp/mgcp_network.c42
-rw-r--r--src/libosmo-legacy-mgcp/mgcp_osmux.c17
-rw-r--r--src/libosmo-legacy-mgcp/mgcp_protocol.c23
-rw-r--r--src/libosmo-legacy-mgcp/mgcp_vty.c74
-rw-r--r--src/libosmo-mgcp-client/Makefile.am2
-rw-r--r--src/libosmo-mgcp-client/mgcp_client.c447
-rw-r--r--src/libosmo-mgcp-client/mgcp_client_fsm.c45
-rw-r--r--src/libosmo-mgcp/Makefile.am1
-rw-r--r--src/libosmo-mgcp/mgcp_codec.c343
-rw-r--r--src/libosmo-mgcp/mgcp_conn.c54
-rw-r--r--src/libosmo-mgcp/mgcp_network.c75
-rw-r--r--src/libosmo-mgcp/mgcp_osmux.c35
-rw-r--r--src/libosmo-mgcp/mgcp_protocol.c301
-rw-r--r--src/libosmo-mgcp/mgcp_sdp.c258
-rw-r--r--src/libosmo-mgcp/mgcp_stat.c31
-rw-r--r--src/libosmo-mgcp/mgcp_vty.c63
-rw-r--r--tests/mgcp/mgcp_test.c235
-rw-r--r--tests/mgcp/mgcp_test.ok35
-rw-r--r--tests/mgcp_client/mgcp_client_test.c184
-rw-r--r--tests/mgcp_client/mgcp_client_test.err4
-rw-r--r--tests/mgcp_client/mgcp_client_test.ok101
38 files changed, 2081 insertions, 499 deletions
diff --git a/.gitignore b/.gitignore
index a9e6b84..7633c73 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,6 +38,7 @@ missing
stamp-h1
libtool
ltmain.sh
+m4/*.m4
# git-version-gen magic
.tarball-version
diff --git a/configure.ac b/configure.ac
index 0ded288..29390c6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -39,10 +39,10 @@ AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
AC_SUBST(LIBRARY_DL)
-PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.11.0)
-PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.11.0)
-PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0)
-PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.2.0)
+PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.12.0)
+PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.12.0)
+PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.12.0)
+PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.3.0)
AC_ARG_ENABLE(sanitize,
[AS_HELP_STRING(
diff --git a/git-version-gen b/git-version-gen
index 8e59c5a..42cf3d2 100755
--- a/git-version-gen
+++ b/git-version-gen
@@ -92,8 +92,8 @@ fi
if test -n "$v"
then
: # use $v
-elif test -d ./.git \
- && v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
+elif
+ v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
|| git describe --abbrev=4 HEAD 2>/dev/null` \
&& case $v in
[0-9]*) ;;
diff --git a/include/osmocom/legacy_mgcp/mgcp.h b/include/osmocom/legacy_mgcp/mgcp.h
index 147a0d5..7490e37 100644
--- a/include/osmocom/legacy_mgcp/mgcp.h
+++ b/include/osmocom/legacy_mgcp/mgcp.h
@@ -243,6 +243,12 @@ struct mgcp_config {
* message.
*/
uint16_t osmux_dummy;
+
+ /* Use a jitterbuffer on the bts-side receiver */
+ bool bts_use_jibuf;
+ /* Minimum and maximum buffer size for the jitter buffer, in ms */
+ uint32_t bts_jitter_delay_min;
+ uint32_t bts_jitter_delay_max;
};
/* config management */
diff --git a/include/osmocom/legacy_mgcp/mgcp_internal.h b/include/osmocom/legacy_mgcp/mgcp_internal.h
index dcc75f1..956bee0 100644
--- a/include/osmocom/legacy_mgcp/mgcp_internal.h
+++ b/include/osmocom/legacy_mgcp/mgcp_internal.h
@@ -25,6 +25,7 @@
#include <string.h>
#include <osmocom/core/select.h>
+#include <osmocom/netif/jibuf.h>
#define CI_UNUSED 0
@@ -198,6 +199,14 @@ struct mgcp_endpoint {
uint32_t octets;
} stats;
} osmux;
+
+ /* Jitter buffer */
+ struct osmo_jibuf* bts_jb;
+ /* Use a jitterbuffer on the bts-side receiver */
+ bool bts_use_jibuf;
+ /* Minimum and maximum buffer size for the jitter buffer, in ms */
+ uint32_t bts_jitter_delay_min;
+ uint32_t bts_jitter_delay_max;
};
#define for_each_line(line, save) \
@@ -335,3 +344,8 @@ static inline const char *mgcp_bts_src_addr(struct mgcp_endpoint *endp)
}
int mgcp_msg_terminate_nul(struct msgb *msg);
+
+/**
+ * Internal jitter buffer related
+ */
+void mgcp_dejitter_udp_send(struct msgb *msg, void *data);
diff --git a/include/osmocom/mgcp/Makefile.am b/include/osmocom/mgcp/Makefile.am
index 7e297e4..65ca670 100644
--- a/include/osmocom/mgcp/Makefile.am
+++ b/include/osmocom/mgcp/Makefile.am
@@ -5,5 +5,6 @@ noinst_HEADERS = \
mgcp_stat.h \
mgcp_endp.h \
mgcp_sdp.h \
+ mgcp_codec.h \
debug.h \
$(NULL)
diff --git a/include/osmocom/mgcp/mgcp.h b/include/osmocom/mgcp/mgcp.h
index bdc8f87..bdc86fc 100644
--- a/include/osmocom/mgcp/mgcp.h
+++ b/include/osmocom/mgcp/mgcp.h
@@ -74,11 +74,13 @@ typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
typedef int (*mgcp_processing)(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *dst_end,
char *data, int *len, int buf_size);
-typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp,
- struct mgcp_rtp_end *dst_end,
- struct mgcp_rtp_end *src_end);
struct mgcp_conn_rtp;
+
+typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp,
+ struct mgcp_conn_rtp *conn_dst,
+ struct mgcp_conn_rtp *conn_src);
+
typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp,
int *payload_type,
const char**subtype_name,
diff --git a/include/osmocom/mgcp/mgcp_codec.h b/include/osmocom/mgcp/mgcp_codec.h
new file mode 100644
index 0000000..f8d5e70
--- /dev/null
+++ b/include/osmocom/mgcp/mgcp_codec.h
@@ -0,0 +1,6 @@
+#pragma once
+
+void mgcp_codec_summary(struct mgcp_conn_rtp *conn);
+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);
+int mgcp_codec_decide(struct mgcp_conn_rtp *conn);
diff --git a/include/osmocom/mgcp/mgcp_common.h b/include/osmocom/mgcp/mgcp_common.h
index d23339f..eb6564f 100644
--- a/include/osmocom/mgcp/mgcp_common.h
+++ b/include/osmocom/mgcp/mgcp_common.h
@@ -82,4 +82,8 @@ static inline int mgcp_msg_terminate_nul(struct msgb *msg)
/* A prefix to denote the virtual trunk (RTP on both ends) */
#define MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK "rtpbridge/"
+/* Maximal number of payload types / codecs that can be negotiated via SDP at
+ * at once. */
+#define MGCP_MAX_CODECS 10
+
#endif
diff --git a/include/osmocom/mgcp/mgcp_conn.h b/include/osmocom/mgcp/mgcp_conn.h
index e2a423f..3da7334 100644
--- a/include/osmocom/mgcp/mgcp_conn.h
+++ b/include/osmocom/mgcp/mgcp_conn.h
@@ -27,6 +27,17 @@
#include <osmocom/core/linuxlist.h>
#include <inttypes.h>
+/* RTP connection related counters */
+enum {
+ IN_STREAM_ERR_TSTMP_CTR,
+ OUT_STREAM_ERR_TSTMP_CTR,
+ RTP_PACKETS_RX_CTR,
+ RTP_OCTETS_RX_CTR,
+ RTP_PACKETS_TX_CTR,
+ RTP_OCTETS_TX_CTR,
+ RTP_DROPPED_PACKETS_CTR
+};
+
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
enum mgcp_conn_type type, char *name);
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id);
diff --git a/include/osmocom/mgcp/mgcp_endp.h b/include/osmocom/mgcp/mgcp_endp.h
index a486dcd..3876794 100644
--- a/include/osmocom/mgcp/mgcp_endp.h
+++ b/include/osmocom/mgcp/mgcp_endp.h
@@ -42,13 +42,13 @@ typedef void (*mgcp_cleanup_cp) (struct mgcp_endpoint *endp,
/*! MGCP endpoint properties */
struct mgcp_endpoint_type {
- /*!< maximum number of connections */
+ /*! maximum number of connections */
int max_conns;
- /*!< callback that defines how to dispatch incoming RTP data */
+ /*! callback that defines how to dispatch incoming RTP data */
mgcp_dispatch_rtp_cb dispatch_rtp_cb;
- /*!< callback that implements endpoint specific cleanup actions */
+ /*! callback that implements endpoint specific cleanup actions */
mgcp_cleanup_cp cleanup_cb;
};
@@ -63,31 +63,31 @@ extern const struct mgcp_endpoint_typeset ep_typeset;
/*! MGCP endpoint model */
struct mgcp_endpoint {
- /*!< Call identifier string (as supplied by the call agant) */
+ /*! Call identifier string (as supplied by the call agant) */
char *callid;
- /*!< Local connection options (see mgcp_intermal.h) */
+ /*! Local connection options (see mgcp_internal.h) */
struct mgcp_lco local_options;
- /*!< List with connections active on this endpoint */
+ /*! List with connections active on this endpoint */
struct llist_head conns;
- /*!< Backpointer to the MGW configuration */
+ /*! Backpointer to the MGW configuration */
struct mgcp_config *cfg;
- /*!< Backpointer to the Trunk specific configuration */
+ /*! Backpointer to the Trunk specific configuration */
struct mgcp_trunk_config *tcfg;
- /*!< Endpoint properties (see above) */
+ /*! Endpoint properties (see above) */
const struct mgcp_endpoint_type *type;
- /*!< Last MGCP transmission (in case re-transmission is required) */
+ /*! Last MGCP transmission (in case re-transmission is required) */
char *last_trans;
- /*!< Last MGCP response (in case re-transmission is required) */
+ /*! Last MGCP response (in case re-transmission is required) */
char *last_response;
- /*!< Memorize if this endpoint was choosen by the MGW (wildcarded, true)
+ /*! Memorize if this endpoint was choosen by the MGW (wildcarded, true)
* or if the user has choosen the particular endpoint explicitly. */
bool wildcarded_req;
};
diff --git a/include/osmocom/mgcp/mgcp_internal.h b/include/osmocom/mgcp/mgcp_internal.h
index 0da2c56..06883dd 100644
--- a/include/osmocom/mgcp/mgcp_internal.h
+++ b/include/osmocom/mgcp/mgcp_internal.h
@@ -28,6 +28,7 @@
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/counter.h>
+#include <osmocom/core/rate_ctr.h>
#define CI_UNUSED 0
@@ -45,7 +46,7 @@ struct mgcp_rtp_stream_state {
uint32_t ssrc;
uint16_t last_seq;
uint32_t last_timestamp;
- uint32_t err_ts_counter;
+ struct rate_ctr *err_ts_ctr;
int32_t last_tsdelta;
uint32_t last_arrival_time;
};
@@ -98,28 +99,25 @@ struct mgcp_rtp_codec {
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
struct mgcp_rtp_end {
- /* statistics */
- struct {
- unsigned int packets_rx;
- unsigned int octets_rx;
- unsigned int packets_tx;
- unsigned int octets_tx;
- unsigned int dropped_packets;
- } stats;
-
/* local IP address of the RTP socket */
struct in_addr addr;
/* in network byte order */
int rtp_port, rtcp_port;
- /* audio codec information */
- struct mgcp_rtp_codec codec;
- struct mgcp_rtp_codec alt_codec; /* TODO/XXX: make it generic */
+ /* currently selected audio codec */
+ struct mgcp_rtp_codec *codec;
+
+ /* array with assigned audio codecs to choose from (SDP) */
+ struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS];
+
+ /* number of assigned audio codecs (SDP) */
+ unsigned int codecs_assigned;
/* per endpoint data */
int frames_per_packet;
uint32_t packet_duration_ms;
+ int maximum_packet_time; /* -1: not set */
char *fmtp_extra;
/* are we transmitting packets (1) or dropping (0) outbound packets */
int output_enabled;
@@ -130,8 +128,6 @@ struct mgcp_rtp_end {
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
int force_aligned_timing;
- /* FIXME: not used anymore, used to be [external] transcoding related */
- void *rtp_process_data;
/* Each end has a separate socket for RTP and RTCP */
struct osmo_fd rtp;
@@ -202,6 +198,8 @@ struct mgcp_conn_rtp {
uint32_t octets;
} stats;
} osmux;
+
+ struct rate_ctr_group *rate_ctr_group;
};
/*! Connection type, specifies which member of the union "u" in mgcp_conn
@@ -212,33 +210,33 @@ enum mgcp_conn_type {
/*! MGCP connection (untyped) */
struct mgcp_conn {
- /*!< list head */
+ /*! list head */
struct llist_head entry;
- /*!< Backpointer to the endpoint where the conn belongs to */
+ /*! Backpointer to the endpoint where the conn belongs to */
struct mgcp_endpoint *endp;
- /*!< type of the connection (union) */
+ /*! type of the connection (union) */
enum mgcp_conn_type type;
- /*!< mode of the connection */
+ /*! mode of the connection */
enum mgcp_connection_mode mode;
- /*!< copy of the mode to restore the original setting (VTY) */
+ /*! copy of the mode to restore the original setting (VTY) */
enum mgcp_connection_mode mode_orig;
- /*!< connection id to identify the connection */
+ /*! connection id to identify the connection */
char id[MGCP_CONN_ID_LENGTH];
- /*!< human readable name (vty, logging) */
+ /*! human readable name (vty, logging) */
char name[256];
- /*!< union with connection description */
+ /*! union with connection description */
union {
struct mgcp_conn_rtp rtp;
} u;
- /*!< pointer to optional private data */
+ /*! pointer to optional private data */
void *priv;
};
@@ -280,6 +278,8 @@ static inline int endp_back_channel(int endpoint)
struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index);
struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index);
+char *get_lco_identifier(const char *options);
+int check_local_cx_options(void *ctx, const char *options);
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
struct mgcp_rtp_end *rtp);
uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
@@ -290,8 +290,8 @@ int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end
char *data, int *len, int buf_size);
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
- struct mgcp_rtp_end *dst_end,
- struct mgcp_rtp_end *src_end);
+ struct mgcp_conn_rtp *conn_dst,
+ struct mgcp_conn_rtp *conn_src);
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
int *payload_type,
diff --git a/include/osmocom/mgcp/mgcp_sdp.h b/include/osmocom/mgcp/mgcp_sdp.h
index da23cba..950092e 100644
--- a/include/osmocom/mgcp/mgcp_sdp.h
+++ b/include/osmocom/mgcp/mgcp_sdp.h
@@ -21,15 +21,11 @@
*/
#pragma once
-#include <osmocom/mgcp/mgcp_sdp.h>
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
struct mgcp_conn_rtp *conn,
struct mgcp_parse_data *p);
-int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
- int payload_type, const char *audio_name);
-
int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
const struct mgcp_conn_rtp *conn, struct msgb *sdp,
const char *addr);
diff --git a/include/osmocom/mgcp/mgcp_stat.h b/include/osmocom/mgcp/mgcp_stat.h
index b6c73fa..0bde8cf 100644
--- a/include/osmocom/mgcp/mgcp_stat.h
+++ b/include/osmocom/mgcp/mgcp_stat.h
@@ -30,8 +30,7 @@
void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn);
/* Exposed for test purposes only, do not use actively */
-void calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *,
- uint32_t *expected, int *loss);
+void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss);
/* Exposed for test purposes only, do not use actively */
uint32_t calc_jitter(struct mgcp_rtp_state *);
diff --git a/include/osmocom/mgcp_client/mgcp_client.h b/include/osmocom/mgcp_client/mgcp_client.h
index 73f3bba..19c684d 100644
--- a/include/osmocom/mgcp_client/mgcp_client.h
+++ b/include/osmocom/mgcp_client/mgcp_client.h
@@ -26,6 +26,33 @@ struct mgcp_client_conf {
typedef unsigned int mgcp_trans_id_t;
+/*! Enumeration of the codec types that mgcp_client is able to handle. */
+enum mgcp_codecs {
+ CODEC_PCMU_8000_1 = 0,
+ CODEC_GSM_8000_1 = 3,
+ CODEC_PCMA_8000_1 = 8,
+ CODEC_G729_8000_1 = 18,
+ CODEC_GSMEFR_8000_1 = 110,
+ CODEC_GSMHR_8000_1 = 111,
+ CODEC_AMR_8000_1 = 112,
+ CODEC_AMRWB_16000_1 = 113,
+};
+/* Note: when new codec types are added, the corresponding value strings
+ * in mgcp_client.c (codec_table) must be updated as well. Enumerations
+ * in enum mgcp_codecs must correspond to a valid payload type. However,
+ * this is an internal assumption that is made to avoid lookup tables.
+ * The API-User should not rely on this coincidence! */
+
+/*! Structure to build a payload type map to allow the defiition custom payload
+ * types. */
+struct ptmap {
+ /*! codec for which a payload type number should be defined */
+ enum mgcp_codecs codec;
+
+ /*! payload type number (96-127) */
+ unsigned int pt;
+};
+
struct mgcp_response_head {
int response_code;
mgcp_trans_id_t trans_id;
@@ -39,6 +66,11 @@ struct mgcp_response {
struct mgcp_response_head head;
uint16_t audio_port;
char audio_ip[INET_ADDRSTRLEN];
+ unsigned int ptime;
+ enum mgcp_codecs codecs[MGCP_MAX_CODECS];
+ unsigned int codecs_len;
+ struct ptmap ptmap[MGCP_MAX_CODECS];
+ unsigned int ptmap_len;
};
enum mgcp_verb {
@@ -66,6 +98,11 @@ struct mgcp_msg {
uint16_t audio_port;
char *audio_ip;
enum mgcp_connection_mode conn_mode;
+ unsigned int ptime;
+ enum mgcp_codecs codecs[MGCP_MAX_CODECS];
+ unsigned int codecs_len;
+ struct ptmap ptmap[MGCP_MAX_CODECS];
+ unsigned int ptmap_len;
};
void mgcp_client_conf_init(struct mgcp_client_conf *conf);
@@ -117,3 +154,9 @@ static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode)
{
return get_value_string(mgcp_client_connection_mode_strs, mode);
}
+
+enum mgcp_codecs map_str_to_codec(const char *str);
+unsigned int map_codec_to_pt(struct ptmap *ptmap, unsigned int ptmap_len,
+ enum mgcp_codecs codec);
+enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
+ unsigned int pt);
diff --git a/include/osmocom/mgcp_client/mgcp_client_fsm.h b/include/osmocom/mgcp_client/mgcp_client_fsm.h
index 7d06178..353baff 100644
--- a/include/osmocom/mgcp_client/mgcp_client_fsm.h
+++ b/include/osmocom/mgcp_client/mgcp_client_fsm.h
@@ -14,20 +14,31 @@
* identifier is supplied it is checked against the internal state to make
* sure it is correct. */
struct mgcp_conn_peer {
- /*!< RTP connection IP-Address (optional, string e.g. "127.0.0.1") */
+ /*! RTP connection IP-Address (optional, string e.g. "127.0.0.1") */
char addr[INET_ADDRSTRLEN];
- /*!< RTP connection IP-Port (optional) */
+ /*! RTP connection IP-Port (optional) */
uint16_t port;
- /*!< RTP endpoint */
+ /*! RTP endpoint */
char endpoint[MGCP_ENDPOINT_MAXLEN];
- /*!< CALL ID (unique per connection) */
+ /*! CALL ID (unique per connection) */
unsigned int call_id;
+
+ /*! RTP packetization interval (optional) */
+ unsigned int ptime;
+
+ /*! RTP codec list (optional) */
+ enum mgcp_codecs codecs[MGCP_MAX_CODECS];
+
+ /*! Number of codecs in RTP codec list (optional) */
+ unsigned int codecs_len;
};
struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, uint32_t parent_term_evt,
uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
void mgcp_conn_delete(struct osmo_fsm_inst *fi);
+
+const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi);
diff --git a/src/libosmo-legacy-mgcp/Makefile.am b/src/libosmo-legacy-mgcp/Makefile.am
index 4ee4e6a..2d6b6dd 100644
--- a/src/libosmo-legacy-mgcp/Makefile.am
+++ b/src/libosmo-legacy-mgcp/Makefile.am
@@ -24,7 +24,7 @@ AM_LDFLAGS = \
# This is not at all related to the release version, but a range of supported
# API versions. Read TODO_RELEASE in the source tree's root!
-LEGACY_MGCP_LIBVERSION=0:1:0
+LEGACY_MGCP_LIBVERSION=1:0:1
lib_LTLIBRARIES = \
libosmo-legacy-mgcp.la \
diff --git a/src/libosmo-legacy-mgcp/mgcp_network.c b/src/libosmo-legacy-mgcp/mgcp_network.c
index 8ccfb42..7b161d2 100644
--- a/src/libosmo-legacy-mgcp/mgcp_network.c
+++ b/src/libosmo-legacy-mgcp/mgcp_network.c
@@ -584,6 +584,36 @@ static int mgcp_send_transcoder(struct mgcp_rtp_end *end,
return rc;
}
+void mgcp_dejitter_udp_send(struct msgb *msg, void *data)
+{
+ struct mgcp_rtp_end *rtp_end = (struct mgcp_rtp_end *) data;
+
+ int rc = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr,
+ rtp_end->rtp_port, (char*) msg->data, msg->len);
+ if (rc != msg->len)
+ LOGP(DLMGCP, LOGL_ERROR,
+ "Failed to send data after jitter buffer: %d\n", rc);
+ msgb_free(msg);
+}
+
+static int enqueue_dejitter(struct osmo_jibuf *jb, struct mgcp_rtp_end *rtp_end, char *buf, int len)
+{
+ struct msgb *msg;
+ msg = msgb_alloc(len, "mgcp-jibuf");
+ if (!msg)
+ return -1;
+
+ memcpy(msg->data, buf, len);
+ msgb_put(msg, len);
+
+ if (osmo_jibuf_enqueue(jb, msg) < 0) {
+ rtp_end->dropped_packets += 1;
+ msgb_free(msg);
+ }
+
+ return len;
+}
+
int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
struct sockaddr_in *addr, char *buf, int rc)
{
@@ -591,6 +621,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
struct mgcp_rtp_end *rtp_end;
struct mgcp_rtp_state *rtp_state;
int tap_idx;
+ struct osmo_jibuf *jb;
LOGP(DLMGCP, LOGL_DEBUG,
"endpoint %x dest %s tcfg->audio_loop %d endp->conn_mode %d (== loopback: %d)\n",
@@ -612,10 +643,12 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
rtp_end = &endp->net_end;
rtp_state = &endp->bts_state;
tap_idx = MGCP_TAP_NET_OUT;
+ jb = endp->bts_jb;
} else {
rtp_end = &endp->bts_end;
rtp_state = &endp->net_state;
tap_idx = MGCP_TAP_BTS_OUT;
+ jb = NULL;
}
LOGP(DLMGCP, LOGL_DEBUG,
"endpoint %x dest %s net_end %s %d %d bts_end %s %d %d rtp_end %s %d %d\n",
@@ -680,9 +713,12 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
rtp_state->patched_first_rtp_payload = true;
}
- rc = mgcp_udp_send(rtp_end->rtp.fd,
- &rtp_end->addr,
- rtp_end->rtp_port, buf, len);
+ if (jb)
+ rc = enqueue_dejitter(jb, rtp_end, buf, len);
+ else
+ rc = mgcp_udp_send(rtp_end->rtp.fd,
+ &rtp_end->addr,
+ rtp_end->rtp_port, buf, len);
if (rc <= 0)
return rc;
diff --git a/src/libosmo-legacy-mgcp/mgcp_osmux.c b/src/libosmo-legacy-mgcp/mgcp_osmux.c
index 743d3f9..b53e2b5 100644
--- a/src/libosmo-legacy-mgcp/mgcp_osmux.c
+++ b/src/libosmo-legacy-mgcp/mgcp_osmux.c
@@ -267,7 +267,6 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
{
struct msgb *msg;
struct osmux_hdr *osmuxh;
- struct llist_head list;
struct sockaddr_in addr;
struct mgcp_config *cfg = ofd->data;
uint32_t rem;
@@ -297,8 +296,7 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
endp->osmux.stats.chunks++;
rem = msg->len;
- osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
- osmux_tx_sched(&list, scheduled_tx_bts_cb, endp);
+ osmux_xfrm_output_sched(&endp->osmux.out, osmuxh);
}
out:
msgb_free(msg);
@@ -359,7 +357,6 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
{
struct msgb *msg;
struct osmux_hdr *osmuxh;
- struct llist_head list;
struct sockaddr_in addr;
struct mgcp_config *cfg = ofd->data;
uint32_t rem;
@@ -389,8 +386,7 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
endp->osmux.stats.chunks++;
rem = msg->len;
- osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
- osmux_tx_sched(&list, scheduled_tx_net_cb, endp);
+ osmux_xfrm_output_sched(&endp->osmux.out, osmuxh);
}
out:
msgb_free(msg);
@@ -470,9 +466,13 @@ int osmux_enable_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, uint
switch (endp->cfg->role) {
case MGCP_BSC_NAT:
endp->type = MGCP_OSMUX_BSC_NAT;
+ osmux_xfrm_output_set_tx_cb(&endp->osmux.out,
+ scheduled_tx_net_cb, endp);
break;
case MGCP_BSC:
endp->type = MGCP_OSMUX_BSC;
+ osmux_xfrm_output_set_tx_cb(&endp->osmux.out,
+ scheduled_tx_bts_cb, endp);
break;
}
endp->osmux.state = OSMUX_STATE_ENABLED;
@@ -484,6 +484,11 @@ void osmux_disable_endpoint(struct mgcp_endpoint *endp)
{
LOGP(DLMGCP, LOGL_INFO, "Releasing endpoint %u using Osmux CID %u\n",
ENDPOINT_NUMBER(endp), endp->osmux.cid);
+
+ /* We are closing, we don't need pending RTP packets to be transmitted */
+ osmux_xfrm_output_set_tx_cb(&endp->osmux.out, NULL, NULL);
+ osmux_xfrm_output_flush(&endp->osmux.out);
+
osmux_xfrm_input_close_circuit(endp->osmux.in, endp->osmux.cid);
endp->osmux.state = OSMUX_STATE_DISABLED;
endp->osmux.cid = -1;
diff --git a/src/libosmo-legacy-mgcp/mgcp_protocol.c b/src/libosmo-legacy-mgcp/mgcp_protocol.c
index 4e82233..854b4ba 100644
--- a/src/libosmo-legacy-mgcp/mgcp_protocol.c
+++ b/src/libosmo-legacy-mgcp/mgcp_protocol.c
@@ -863,6 +863,11 @@ mgcp_header_done:
goto error2;
}
+ /* Apply Jiter buffer settings for this endpoint, they can be overriden by CRCX policy later */
+ endp->bts_use_jibuf = endp->cfg->bts_use_jibuf;
+ endp->bts_jitter_delay_min = endp->cfg->bts_jitter_delay_min;
+ endp->bts_jitter_delay_max = endp->cfg->bts_jitter_delay_max;
+
endp->allocated = 1;
/* set up RTP media parameters */
@@ -898,6 +903,13 @@ mgcp_header_done:
case MGCP_POLICY_DEFER:
/* stop processing */
create_transcoder(endp);
+ /* Set up jitter buffer if required after policy has updated jibuf endp values */
+ if (endp->bts_use_jibuf) {
+ endp->bts_jb = osmo_jibuf_alloc(tcfg->endpoints);
+ osmo_jibuf_set_min_delay(endp->bts_jb, endp->bts_jitter_delay_min);
+ osmo_jibuf_set_max_delay(endp->bts_jb, endp->bts_jitter_delay_max);
+ osmo_jibuf_set_dequeue_cb(endp->bts_jb, mgcp_dejitter_udp_send, &endp->net_end);
+ }
return NULL;
break;
case MGCP_POLICY_CONT:
@@ -906,6 +918,14 @@ mgcp_header_done:
}
}
+ /* Set up jitter buffer if required after policy has updated jibuf endp values */
+ if (endp->bts_use_jibuf) {
+ endp->bts_jb = osmo_jibuf_alloc(tcfg->endpoints);
+ osmo_jibuf_set_min_delay(endp->bts_jb, endp->bts_jitter_delay_min);
+ osmo_jibuf_set_max_delay(endp->bts_jb, endp->bts_jitter_delay_max);
+ osmo_jibuf_set_dequeue_cb(endp->bts_jb, mgcp_dejitter_udp_send, &endp->net_end);
+ }
+
LOGP(DLMGCP, LOGL_DEBUG, "Creating endpoint on: 0x%x CI: %u port: %u/%u\n",
ENDPOINT_NUMBER(endp), endp->ci,
endp->net_end.local_port, endp->bts_end.local_port);
@@ -1373,6 +1393,9 @@ int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg)
void mgcp_release_endp(struct mgcp_endpoint *endp)
{
LOGP(DLMGCP, LOGL_DEBUG, "Releasing endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp));
+ if (endp->bts_jb)
+ osmo_jibuf_delete(endp->bts_jb);
+ endp->bts_jb = NULL;
endp->ci = CI_UNUSED;
endp->allocated = 0;
diff --git a/src/libosmo-legacy-mgcp/mgcp_vty.c b/src/libosmo-legacy-mgcp/mgcp_vty.c
index 7b11422..be884cb 100644
--- a/src/libosmo-legacy-mgcp/mgcp_vty.c
+++ b/src/libosmo-legacy-mgcp/mgcp_vty.c
@@ -29,6 +29,7 @@
#include <osmocom/legacy_mgcp/vty.h>
#include <string.h>
+#include <inttypes.h>
#define RTCP_OMIT_STR "Drop RTCP packets in both directions\n"
#define RTP_PATCH_STR "Modify RTP packet header in both directions\n"
@@ -164,6 +165,13 @@ static int config_write_mgcp(struct vty *vty)
vty_out(vty, " osmux dummy %s%s",
g_cfg->osmux_dummy ? "on" : "off", VTY_NEWLINE);
}
+ if (g_cfg->bts_use_jibuf)
+ vty_out(vty, " bts-jitter-buffer%s", VTY_NEWLINE);
+ if (g_cfg->bts_jitter_delay_min)
+ vty_out(vty, " bts-jitter-delay-min %"PRIu32"%s", g_cfg->bts_jitter_delay_min, VTY_NEWLINE);
+ if (g_cfg->bts_jitter_delay_max)
+ vty_out(vty, " bts-jitter-delay-max %"PRIu32"%s", g_cfg->bts_jitter_delay_max, VTY_NEWLINE);
+
return CMD_SUCCESS;
}
@@ -241,6 +249,11 @@ DEFUN(show_mcgp, show_mgcp_cmd,
if (g_cfg->osmux)
vty_out(vty, "Osmux used CID: %d%s", osmux_used_cid(), VTY_NEWLINE);
+ vty_out(vty, "Jitter Buffer by default on Uplink : %s%s",
+ g_cfg->bts_use_jibuf ? "on" : "off", VTY_NEWLINE);
+ if (g_cfg->bts_use_jibuf)
+ vty_out(vty, "Jitter Buffer delays: min=%"PRIu32" max=%"PRIu32"%s",
+ g_cfg->bts_jitter_delay_min, g_cfg->bts_jitter_delay_max, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -1344,6 +1357,63 @@ DEFUN(cfg_mgcp_osmux_dummy,
return CMD_SUCCESS;
}
+#define DEJITTER_STR "Uplink Jitter Buffer"
+DEFUN(cfg_mgcp_bts_use_jibuf,
+ cfg_mgcp_bts_use_jibuf_cmd,
+ "bts-jitter-buffer",
+ DEJITTER_STR "\n")
+{
+ g_cfg->bts_use_jibuf = true;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_no_bts_use_jibuf,
+ cfg_mgcp_no_bts_use_jibuf_cmd,
+ "no bts-jitter-buffer",
+ NO_STR DEJITTER_STR "\n")
+{
+ g_cfg->bts_use_jibuf = false;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_bts_jitter_delay_min,
+ cfg_mgcp_bts_jitter_delay_min_cmd,
+ "bts-jitter-buffer-delay-min <1-65535>",
+ DEJITTER_STR " Minimum Delay in ms\n" "Minimum Delay in ms\n")
+{
+ g_cfg->bts_jitter_delay_min = atoi(argv[0]);
+ if (!g_cfg->bts_jitter_delay_min) {
+ vty_out(vty, "bts-jitter-buffer-delay-min cannot be zero.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (g_cfg->bts_jitter_delay_min && g_cfg->bts_jitter_delay_max &&
+ g_cfg->bts_jitter_delay_min > g_cfg->bts_jitter_delay_max) {
+ vty_out(vty, "bts-jitter-buffer-delay-min cannot be bigger than " \
+ "bts-jitter-buffer-delay-max.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_bts_jitter_delay_max,
+ cfg_mgcp_bts_jitter_delay_max_cmd,
+ "bts-jitter-buffer-delay-max <1-65535>",
+ DEJITTER_STR " Maximum Delay in ms\n" "Maximum Delay in ms\n")
+{
+ g_cfg->bts_jitter_delay_max = atoi(argv[0]);
+ if (!g_cfg->bts_jitter_delay_max) {
+ vty_out(vty, "bts-jitter-buffer-delay-max cannot be zero.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (g_cfg->bts_jitter_delay_min && g_cfg->bts_jitter_delay_max &&
+ g_cfg->bts_jitter_delay_min > g_cfg->bts_jitter_delay_max) {
+ vty_out(vty, "bts-jitter-buffer-delay-max cannot be smaller than " \
+ "bts-jitter-buffer-delay-min.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ return CMD_SUCCESS;
+}
+
int mgcp_vty_init(void)
{
install_element_ve(&show_mgcp_cmd);
@@ -1411,6 +1481,10 @@ int mgcp_vty_init(void)
install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd);
install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd);
install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_bts_use_jibuf_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_no_bts_use_jibuf_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_bts_jitter_delay_min_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_bts_jitter_delay_max_cmd);
install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd);
diff --git a/src/libosmo-mgcp-client/Makefile.am b/src/libosmo-mgcp-client/Makefile.am
index 01371d7..d3f8fff 100644
--- a/src/libosmo-mgcp-client/Makefile.am
+++ b/src/libosmo-mgcp-client/Makefile.am
@@ -20,7 +20,7 @@ AM_LDFLAGS = \
# This is not at all related to the release version, but a range of supported
# API versions. Read TODO_RELEASE in the source tree's root!
-MGCP_CLIENT_LIBVERSION=3:0:0
+MGCP_CLIENT_LIBVERSION=4:0:1
lib_LTLIBRARIES = \
libosmo-mgcp-client.la \
diff --git a/src/libosmo-mgcp-client/mgcp_client.c b/src/libosmo-mgcp-client/mgcp_client.c
index e054593..00b52f8 100644
--- a/src/libosmo-mgcp-client/mgcp_client.c
+++ b/src/libosmo-mgcp-client/mgcp_client.c
@@ -36,6 +36,150 @@
#include <unistd.h>
#include <string.h>
+/* Codec descripton for dynamic payload types (SDP) */
+static const struct value_string codec_table[] = {
+ { CODEC_PCMU_8000_1, "PCMU/8000/1" },
+ { CODEC_GSM_8000_1, "GSM/8000/1" },
+ { CODEC_PCMA_8000_1, "PCMA/8000/1" },
+ { CODEC_G729_8000_1, "G729/8000/1" },
+ { CODEC_GSMEFR_8000_1, "GSM-EFR/8000/1" },
+ { CODEC_GSMHR_8000_1, "GSM-HR-08/8000/1" },
+ { CODEC_AMR_8000_1, "AMR/8000/1" },
+ { CODEC_AMRWB_16000_1, "AMR-WB/16000/1" },
+ { 0, NULL },
+};
+
+/* Get encoding name from a full codec string e,g.
+ * ("CODEC/8000/2" => returns "CODEC") */
+static char *extract_codec_name(const char *str)
+{
+ static char buf[64];
+ unsigned int i;
+
+ if (!str)
+ return NULL;
+
+ /* FIXME osmo_strlcpy */
+ osmo_strlcpy(buf, str, sizeof(buf));
+
+ for (i = 0; i < strlen(buf); i++) {
+ if (buf[i] == '/')
+ buf[i] = '\0';
+ }
+
+ return buf;
+}
+
+/*! Map a string to a codec.
+ * \ptmap[in] str input string (e.g "GSM/8000/1", "GSM/8000" or "GSM")
+ * \returns codec that corresponds to the given string representation. */
+enum mgcp_codecs map_str_to_codec(const char *str)
+{
+ unsigned int i;
+ char *codec_name;
+ char str_buf[64];
+
+ osmo_strlcpy(str_buf, extract_codec_name(str), sizeof(str_buf));
+
+ for (i = 0; i < ARRAY_SIZE(codec_table); i++) {
+ codec_name = extract_codec_name(codec_table[i].str);
+ if (!codec_name)
+ continue;
+ if (strcmp(codec_name, str_buf) == 0)
+ return codec_table[i].value;
+ }
+
+ return -1;
+}
+
+/* Check the ptmap for illegal mappings */
+static int check_ptmap(struct ptmap *ptmap)
+{
+ /* Check if there are mappings that leave the IANA assigned dynamic
+ * payload type range. Under normal conditions such mappings should
+ * not occur */
+
+ /* Its ok to have a 1:1 mapping in the statically defined
+ * range, this won't hurt */
+ if (ptmap->codec == ptmap->pt)
+ return 0;
+
+ if (ptmap->codec < 96 || ptmap->codec > 127)
+ goto error;
+ if (ptmap->pt < 96 || ptmap->pt > 127)
+ goto error;
+
+ return 0;
+error:
+ LOGP(DLMGCP, LOGL_ERROR,
+ "ptmap contains illegal mapping: codec=%u maps to pt=%u\n",
+ ptmap->codec, ptmap->pt);
+ return -1;
+}
+
+/*! Map a codec to a payload type.
+ * \ptmap[in] payload pointer to payload type map with specified payload types.
+ * \ptmap[in] ptmap_len length of the payload type map.
+ * \ptmap[in] codec the codec for which the payload type should be looked up.
+ * \returns assigned payload type */
+unsigned int map_codec_to_pt(struct ptmap *ptmap, unsigned int ptmap_len,
+ enum mgcp_codecs codec)
+{
+ unsigned int i;
+
+ /*! Note: If the payload type map is empty or the codec is not found
+ * in the map, then a 1:1 mapping is performed. If the codec falls
+ * into the statically defined range or if the mapping table isself
+ * tries to map to the statically defined range, then the mapping
+ * is also ignored and a 1:1 mapping is performed instead. */
+
+ /* we may return the codec directly since enum mgcp_codecs directly
+ * corresponds to the statićally assigned payload types */
+ if (codec < 96 || codec > 127)
+ return codec;
+
+ for (i = 0; i < ptmap_len; i++) {
+ /* Skip illegal map entries */
+ if (check_ptmap(ptmap) == 0 && ptmap->codec == codec)
+ return ptmap->pt;
+ ptmap++;
+ }
+
+ /* If nothing is found, do not perform any mapping */
+ return codec;
+}
+
+/*! Map a payload type to a codec.
+ * \ptmap[in] payload pointer to payload type map with specified payload types.
+ * \ptmap[in] ptmap_len length of the payload type map.
+ * \ptmap[in] payload type for which the codec should be looked up.
+ * \returns codec that corresponds to the specified payload type */
+enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
+ unsigned int pt)
+{
+ unsigned int i;
+
+ /*! Note: If the payload type map is empty or the payload type is not
+ * found in the map, then a 1:1 mapping is performed. If the payload
+ * type falls into the statically defined range or if the mapping
+ * table isself tries to map to the statically defined range, then
+ * the mapping is also ignored and a 1:1 mapping is performed
+ * instead. */
+
+ /* See also note in map_codec_to_pt() */
+ if (pt < 96 || pt > 127)
+ return pt;
+
+ for (i = 0; i < ptmap_len; i++) {
+ if (check_ptmap(ptmap) == 0 && ptmap->pt == pt)
+ return ptmap->codec;
+ ptmap++;
+ }
+
+ /* If nothing is found, do not perform any mapping */
+ return pt;
+}
+
/*! Initalize MGCP client configuration struct with default values.
* \param[out] conf Client configuration.*/
void mgcp_client_conf_init(struct mgcp_client_conf *conf)
@@ -178,24 +322,116 @@ static bool mgcp_line_is_valid(const char *line)
return true;
}
-/* Parse a line like "m=audio 16002 RTP/AVP 98" */
-static int mgcp_parse_audio_port(struct mgcp_response *r, const char *line)
+/* Parse a line like "m=audio 16002 RTP/AVP 98", extract port and payload types */
+static int mgcp_parse_audio_port_pt(struct mgcp_response *r, char *line)
{
- if (sscanf(line, "m=audio %hu",
- &r->audio_port) != 1)
- goto response_parse_failure;
+ char *pt_str;
+ unsigned int pt;
+ unsigned int count = 0;
+ unsigned int i;
+ /* Extract port information */
+ if (sscanf(line, "m=audio %hu", &r->audio_port) != 1)
+ goto response_parse_failure_port;
if (r->audio_port == 0)
- goto response_parse_failure;
+ goto response_parse_failure_port;
+
+ /* Extract payload types */
+ line = strstr(line, "RTP/AVP ");
+ if (!line)
+ goto exit;
+
+ pt_str = strtok(line, " ");
+ while (1) {
+ /* Do not allow excessive payload types */
+ if (count > ARRAY_SIZE(r->codecs))
+ goto response_parse_failure_pt;
+ pt_str = strtok(NULL, " ");
+ if (!pt_str)
+ break;
+ pt = atoi(pt_str);
+
+ /* Do not allow duplicate payload types */
+ for (i = 0; i < count; i++)
+ if (r->codecs[i] == pt)
+ goto response_parse_failure_pt;
+
+ /* Note: The payload type we store may not necessarly match
+ * the codec types we have defined in enum mgcp_codecs. To
+ * ensure that the end result only contains codec types which
+ * match enum mgcp_codecs, we will go through afterwards and
+ * remap the affected entries with the inrofmation we learn
+ * from rtpmap */
+ r->codecs[count] = pt;
+ count++;
+ }
+
+ r->codecs_len = count;
+
+exit:
return 0;
-response_parse_failure:
+response_parse_failure_port:
+ LOGP(DLMGCP, LOGL_ERROR,
+ "Failed to parse SDP parameter port (%s)\n", line);
+ return -EINVAL;
+
+response_parse_failure_pt:
LOGP(DLMGCP, LOGL_ERROR,
- "Failed to parse MGCP response header (audio port)\n");
+ "Failed to parse SDP parameter payload types (%s)\n", line);
return -EINVAL;
}
+/* Parse a line like "m=audio 16002 RTP/AVP 98", extract port and payload types */
+static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *line)
+{
+ unsigned int pt;
+ char codec_resp[64];
+ unsigned int codec;
+
+
+ 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;
+ }
+
+ 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" */
static int mgcp_parse_audio_ip(struct mgcp_response *r, const char *line)
{
@@ -253,6 +489,7 @@ int mgcp_response_parse_params(struct mgcp_response *r)
int rc;
char *data;
char *data_ptr;
+ int i;
/* Since this functions performs a destructive parsing, we create a
* local copy of the body data */
@@ -277,8 +514,13 @@ int mgcp_response_parse_params(struct mgcp_response *r)
return -EINVAL;
switch (line[0]) {
+ case 'a':
+ rc = mgcp_parse_audio_ptime_rtpmap(r, line);
+ if (rc)
+ goto exit;
+ break;
case 'm':
- rc = mgcp_parse_audio_port(r, line);
+ rc = mgcp_parse_audio_port_pt(r, line);
if (rc)
goto exit;
break;
@@ -293,6 +535,10 @@ int mgcp_response_parse_params(struct mgcp_response *r)
}
}
+ /* See also note in mgcp_parse_audio_port_pt() */
+ for (i = 0; i < r->codecs_len; i++)
+ r->codecs[i] = map_pt_to_codec(r->ptmap, r->ptmap_len, r->codecs[i]);
+
rc = 0;
exit:
talloc_free(data);
@@ -813,6 +1059,119 @@ struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
#define MGCP_AUEP_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT)
#define MGCP_RSIP_MANDATORY 0 /* none */
+/* Helper function for mgcp_msg_gen(): Add LCO information to MGCP message */
+static int add_lco(struct msgb *msg, struct mgcp_msg *mgcp_msg)
+{
+ unsigned int i;
+ int rc = 0;
+ const char *codec;
+ unsigned int pt;
+
+ rc += msgb_printf(msg, "L:");
+
+ if (mgcp_msg->ptime)
+ rc += msgb_printf(msg, " p:%u,", mgcp_msg->ptime);
+
+ if (mgcp_msg->codecs_len) {
+ rc += msgb_printf(msg, " a:");
+ for (i = 0; i < mgcp_msg->codecs_len; i++) {
+ pt = mgcp_msg->codecs[i];
+ codec = get_value_string_or_null(codec_table, pt);
+
+ /* Note: Use codec descriptors from enum mgcp_codecs
+ * in mgcp_client only! */
+ OSMO_ASSERT(codec);
+ rc += msgb_printf(msg, "%s", extract_codec_name(codec));
+ if (i < mgcp_msg->codecs_len - 1)
+ rc += msgb_printf(msg, ";");
+ }
+ rc += msgb_printf(msg, ",");
+ }
+
+ rc += msgb_printf(msg, " nt:IN\r\n");
+
+ return rc;
+}
+
+/* Helper function for mgcp_msg_gen(): Add SDP information to MGCP message */
+static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_client *mgcp)
+{
+ unsigned int i;
+ int rc = 0;
+ char local_ip[INET_ADDRSTRLEN];
+ const char *codec;
+ unsigned int pt;
+
+ /* Add separator to mark the beginning of the SDP block */
+ rc += msgb_printf(msg, "\r\n");
+
+ /* Add SDP protocol version */
+ rc += msgb_printf(msg, "v=0\r\n");
+
+ /* Determine local IP-Address */
+ if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) < 0) {
+ LOGP(DLMGCP, LOGL_ERROR,
+ "Could not determine local IP-Address!\n");
+ msgb_free(msg);
+ return -2;
+ }
+
+ /* Add owner/creator (SDP) */
+ rc += msgb_printf(msg, "o=- %x 23 IN IP4 %s\r\n",
+ mgcp_msg->call_id, local_ip);
+
+ /* Add session name (none) */
+ rc += msgb_printf(msg, "s=-\r\n");
+
+ /* Add RTP address and port */
+ if (mgcp_msg->audio_port == 0) {
+ LOGP(DLMGCP, LOGL_ERROR,
+ "Invalid port number, can not generate MGCP message\n");
+ msgb_free(msg);
+ return -2;
+ }
+ if (strlen(mgcp_msg->audio_ip) <= 0) {
+ LOGP(DLMGCP, LOGL_ERROR,
+ "Empty ip address, can not generate MGCP message\n");
+ msgb_free(msg);
+ return -2;
+ }
+ rc += msgb_printf(msg, "c=IN IP4 %s\r\n", mgcp_msg->audio_ip);
+
+ /* Add time description, active time (SDP) */
+ rc += msgb_printf(msg, "t=0 0\r\n");
+
+ rc += msgb_printf(msg, "m=audio %u RTP/AVP", mgcp_msg->audio_port);
+ for (i = 0; i < mgcp_msg->codecs_len; i++) {
+ pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
+ rc += msgb_printf(msg, " %u", pt);
+
+ }
+ rc += msgb_printf(msg, "\r\n");
+
+ for (i = 0; i < mgcp_msg->codecs_len; i++) {
+ pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
+
+ /* Note: Only dynamic payload type from the range 96-127
+ * require to be explained further via rtpmap. All others
+ * are implcitly definedby the number in m=audio */
+ if (pt >= 96 && pt <= 127) {
+ codec = get_value_string_or_null(codec_table, mgcp_msg->codecs[i]);
+
+ /* Note: Use codec descriptors from enum mgcp_codecs
+ * in mgcp_client only! */
+ OSMO_ASSERT(codec);
+
+ rc += msgb_printf(msg, "a=rtpmap:%u %s\r\n", pt, codec);
+ }
+ }
+
+ if (mgcp_msg->ptime)
+ rc += msgb_printf(msg, "a=ptime:%u\r\n", mgcp_msg->ptime);
+
+ return rc;
+}
+
/*! Generate an MGCP message
* \param[in] mgcp MGCP client descriptor.
* \param[in] mgcp_msg Message description
@@ -823,7 +1182,8 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
uint32_t mandatory_mask;
struct msgb *msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
int rc = 0;
- char local_ip[INET_ADDRSTRLEN];
+ int rc_sdp;
+ bool use_sdp = false;
msg->l2h = msg->data;
msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
@@ -902,9 +1262,17 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
rc += msgb_printf(msg, "I: %s\r\n", mgcp_msg->conn_id);
}
- /* Add local connection options */
- if (mgcp_msg->verb == MGCP_VERB_CRCX)
- rc += msgb_printf(msg, "L: p:20, a:AMR, nt:IN\r\n");
+ /* Using SDP makes sense when a valid IP/Port combination is specifiec,
+ * if we do not know this information yet, we fall back to LCO */
+ if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP
+ && mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT)
+ use_sdp = true;
+
+ /* Add local connection options (LCO) */
+ if (!use_sdp
+ && (mgcp_msg->verb == MGCP_VERB_CRCX
+ || mgcp_msg->verb == MGCP_VERB_MDCX))
+ rc += add_lco(msg, mgcp_msg);
/* Add mode */
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_MODE)
@@ -912,52 +1280,15 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
msgb_printf(msg, "M: %s\r\n",
mgcp_client_cmode_name(mgcp_msg->conn_mode));
- /* Add SDP body */
- if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP
- && mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT) {
-
- /* Add separator to mark the beginning of the SDP block */
- rc += msgb_printf(msg, "\r\n");
-
- /* Add SDP protocol version */
- rc += msgb_printf(msg, "v=0\r\n");
-
- /* Determine local IP-Address */
- if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) < 0) {
- LOGP(DLMGCP, LOGL_ERROR,
- "Could not determine local IP-Address!\n");
- msgb_free(msg);
- return NULL;
- }
-
- /* Add owner/creator (SDP) */
- rc += msgb_printf(msg, "o=- %x 23 IN IP4 %s\r\n",
- mgcp_msg->call_id, local_ip);
-
- /* Add session name (none) */
- rc += msgb_printf(msg, "s=-\r\n");
-
- /* Add RTP address and port */
- if (mgcp_msg->audio_port == 0) {
- LOGP(DLMGCP, LOGL_ERROR,
- "Invalid port number, can not generate MGCP message\n");
- msgb_free(msg);
+ /* Add session description protocol (SDP) */
+ if (use_sdp
+ && (mgcp_msg->verb == MGCP_VERB_CRCX
+ || mgcp_msg->verb == MGCP_VERB_MDCX)) {
+ rc_sdp = add_sdp(msg, mgcp_msg, mgcp);
+ if (rc_sdp == -2)
return NULL;
- }
- if (strlen(mgcp_msg->audio_ip) <= 0) {
- LOGP(DLMGCP, LOGL_ERROR,
- "Empty ip address, can not generate MGCP message\n");
- msgb_free(msg);
- return NULL;
- }
- rc += msgb_printf(msg, "c=IN IP4 %s\r\n", mgcp_msg->audio_ip);
-
- /* Add time description, active time (SDP) */
- rc += msgb_printf(msg, "t=0 0\r\n");
-
- rc +=
- msgb_printf(msg, "m=audio %u RTP/AVP 255\r\n",
- mgcp_msg->audio_port);
+ else
+ rc += rc_sdp;
}
if (rc != 0) {
diff --git a/src/libosmo-mgcp-client/mgcp_client_fsm.c b/src/libosmo-mgcp-client/mgcp_client_fsm.c
index 3c755be..fe1590f 100644
--- a/src/libosmo-mgcp-client/mgcp_client_fsm.c
+++ b/src/libosmo-mgcp-client/mgcp_client_fsm.c
@@ -112,9 +112,12 @@ static struct msgb *make_crcx_msg_bind(struct mgcp_ctx *mgcp_ctx)
.verb = MGCP_VERB_CRCX,
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
.call_id = mgcp_ctx->conn_peer_local.call_id,
- .conn_mode = MGCP_CONN_LOOPBACK,
+ .conn_mode = MGCP_CONN_RECV_ONLY,
+ .ptime = mgcp_ctx->conn_peer_local.ptime,
+ .codecs_len = mgcp_ctx->conn_peer_local.codecs_len
};
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN);
+ memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
}
@@ -124,15 +127,19 @@ static struct msgb *make_crcx_msg_bind_connect(struct mgcp_ctx *mgcp_ctx)
struct mgcp_msg mgcp_msg;
mgcp_msg = (struct mgcp_msg) {
- .verb = MGCP_VERB_CRCX,.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
- MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
- MGCP_MSG_PRESENCE_AUDIO_PORT),
+ .verb = MGCP_VERB_CRCX,
+ .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
+ MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
+ MGCP_MSG_PRESENCE_AUDIO_PORT),
.call_id = mgcp_ctx->conn_peer_local.call_id,
.conn_mode = MGCP_CONN_RECV_SEND,
.audio_ip = mgcp_ctx->conn_peer_local.addr,
.audio_port = mgcp_ctx->conn_peer_local.port,
+ .ptime = mgcp_ctx->conn_peer_local.ptime,
+ .codecs_len = mgcp_ctx->conn_peer_local.codecs_len
};
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN);
+ memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
}
@@ -150,8 +157,11 @@ static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
.conn_mode = MGCP_CONN_RECV_SEND,
.audio_ip = mgcp_ctx->conn_peer_local.addr,
.audio_port = mgcp_ctx->conn_peer_local.port,
+ .ptime = mgcp_ctx->conn_peer_local.ptime,
+ .codecs_len = mgcp_ctx->conn_peer_local.codecs_len
};
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN);
+ memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
/* Note: We take the endpoint and the call_id from the remote
* connection info, because we can be confident that the
@@ -215,6 +225,14 @@ static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
}
}
+/* Return the CI that the MGW allocated during CRCX response. This is purely informational for logging
+ * and identity tracking; the mgcp_conn_*() functions take care of using the right CI internally. */
+const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi)
+{
+ struct mgcp_ctx *mgcp_ctx = fi->priv;
+ return mgcp_ctx->conn_id;
+}
+
static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv)
{
struct osmo_fsm_inst *fi = priv;
@@ -476,7 +494,7 @@ static void fsm_cleanup_cb(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause ca
* mgcp_conn_delete() to instruct the FSM to perform a graceful exit */
if (strlen(mgcp_ctx->conn_id)) {
LOGPFSML(fi, LOGL_ERROR,
- "MGW/DLCX: aprupt FSM termination with connections still present, sending unconditional DLCX...\n");
+ "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);
@@ -548,7 +566,7 @@ static struct osmo_fsm fsm_mgcp_client = {
/*! allocate FSM, and create a new connection on the MGW.
* \param[in] mgcp MGCP client descriptor.
- * \param[in] mgcpparent_fi Parent FSM instance.
+ * \param[in] parent_fi Parent FSM instance.
* \param[in] parent_term_evt Event to be sent to parent when terminating.
* \param[in] parent_evt Event to be sent to parent when operation is done.
* \param[in] conn_peer Connection parameters (ip, port...).
@@ -565,7 +583,7 @@ struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm
OSMO_ASSERT(mgcp);
OSMO_ASSERT(conn_peer);
- /* Check if IP/Port informstaion in conn info makes sense */
+ /* Check if IP/Port information in conn info makes sense */
if (conn_peer->port && inet_aton(conn_peer->addr, &ip_test) == 0)
return NULL;
@@ -622,17 +640,24 @@ int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_
OSMO_ASSERT(fi->state != ST_DLCX_RESP);
/* Check if IP/Port parameters make sense */
- if (conn_peer->port == 0)
+ if (conn_peer->port == 0) {
+ LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, port == 0\n");
return -EINVAL;
- if (inet_aton(conn_peer->addr, &ip_test) == 0)
+ }
+ if (inet_aton(conn_peer->addr, &ip_test) == 0) {
+ LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, IP address == 0.0.0.0\n");
return -EINVAL;
+ }
/*! The user may supply an endpoint identifier in conn_peer. The
* identifier is then checked. This check is optional. Later steps do
* not depend on the endpoint identifier supplied here because it is
* already implicitly known from the CRCX phase. */
- if (strlen(conn_peer->endpoint) && strcmp(conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint))
+ if (strlen(conn_peer->endpoint) && strcmp(conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint)) {
+ LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, endpoint mismatches: requested %s, should be %s\n",
+ conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint);
return -EINVAL;
+ }
/*! Note: The call-id is implicitly known from the previous CRCX and
* will not be checked even when it is set in conn_peer. */
diff --git a/src/libosmo-mgcp/Makefile.am b/src/libosmo-mgcp/Makefile.am
index a98c391..587bdd4 100644
--- a/src/libosmo-mgcp/Makefile.am
+++ b/src/libosmo-mgcp/Makefile.am
@@ -35,6 +35,7 @@ libosmo_mgcp_a_SOURCES = \
mgcp_vty.c \
mgcp_osmux.c \
mgcp_sdp.c \
+ mgcp_codec.c \
mgcp_msg.c \
mgcp_conn.c \
mgcp_stat.c \
diff --git a/src/libosmo-mgcp/mgcp_codec.c b/src/libosmo-mgcp/mgcp_codec.c
new file mode 100644
index 0000000..2ce90dd
--- /dev/null
+++ b/src/libosmo-mgcp/mgcp_codec.c
@@ -0,0 +1,343 @@
+/*
+ * (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009-2014 by On-Waves
+ * All Rights Reserved
+ *
+ * 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 <osmocom/mgcp/mgcp_internal.h>
+#include <osmocom/mgcp/mgcp_endp.h>
+#include <errno.h>
+
+/* Helper function to dump codec information of a specified codec to a printable
+ * string, used by dump_codec_summary() */
+static char *dump_codec(struct mgcp_rtp_codec *codec)
+{
+ static char str[256];
+ char *pt_str;
+
+ if (codec->payload_type > 76)
+ pt_str = "DYNAMIC";
+ else if (codec->payload_type > 72)
+ pt_str = "RESERVED <!>";
+ else if (codec->payload_type != PTYPE_UNDEFINED)
+ pt_str = codec->subtype_name;
+ else
+ pt_str = "INVALID <!>";
+
+ snprintf(str, sizeof(str), "(pt:%i=%s, audio:%s subt=%s, rate=%u, ch=%i, t=%u/%u)", codec->payload_type, pt_str,
+ codec->audio_name, codec->subtype_name, codec->rate, codec->channels, codec->frame_duration_num,
+ codec->frame_duration_den);
+ return str;
+}
+
+/*! Dump a summary of all negotiated codecs to debug log
+ * \param[in] conn related rtp-connection. */
+void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
+{
+ struct mgcp_rtp_end *rtp;
+ unsigned int i;
+ struct mgcp_rtp_codec *codec;
+ struct mgcp_endpoint *endp;
+
+ rtp = &conn->end;
+ endp = conn->conn->endp;
+
+ if (rtp->codecs_assigned == 0) {
+ LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x conn:%s no codecs available\n", ENDPOINT_NUMBER(endp),
+ mgcp_conn_dump(conn->conn));
+ return;
+ }
+
+ /* Store parsed codec information */
+ for (i = 0; i < rtp->codecs_assigned; i++) {
+ codec = &rtp->codecs[i];
+
+ LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x conn:%s codecs[%u]:%s", ENDPOINT_NUMBER(endp),
+ mgcp_conn_dump(conn->conn), i, dump_codec(codec));
+
+ if (codec == rtp->codec)
+ LOGPC(DLMGCP, LOGL_DEBUG, " [selected]");
+
+ LOGPC(DLMGCP, LOGL_DEBUG, "\n");
+ }
+}
+
+/* Initalize or reset codec information with default data. */
+void codec_init(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;
+}
+
+/*! 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));
+ conn->end.codecs_assigned = 0;
+ conn->end.codec = NULL;
+}
+
+/* Set members of struct mgcp_rtp_codec, extrapolate in missing information */
+static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
+ int payload_type, const char *audio_name, unsigned int pt_offset)
+{
+ int rate;
+ int channels;
+ char audio_codec[64];
+
+ /* Initalize the codec struct with some default data to begin with */
+ codec_init(codec);
+
+ 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)
+ goto error;
+
+ codec->payload_type = payload_type;
+ }
+
+ /* When no audio name is given, we are forced to use the payload
+ * type to generate the audio name. This is only possible for
+ * non dynamic payload types, which are statically defined */
+ if (!audio_name) {
+ switch (payload_type) {
+ case 0:
+ audio_name = talloc_strdup(ctx, "PCMU/8000/1");
+ break;
+ case 3:
+ audio_name = talloc_strdup(ctx, "GSM/8000/1");
+ break;
+ case 8:
+ audio_name = talloc_strdup(ctx, "PCMA/8000/1");
+ break;
+ case 18:
+ audio_name = talloc_strdup(ctx, "G729/8000/1");
+ break;
+ default:
+ /* 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 */
+ goto error;
+ }
+ }
+
+ /* 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))
+ 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)
+ goto error;
+
+ /* Note: We only accept configurations with one audio channel! */
+ if (channels != 1)
+ goto error;
+
+ codec->rate = rate;
+ codec->channels = channels;
+ codec->subtype_name = talloc_strdup(ctx, audio_codec);
+ codec->audio_name = talloc_strdup(ctx, audio_name);
+ codec->payload_type = payload_type;
+
+ if (!strcmp(audio_codec, "G729")) {
+ codec->frame_duration_num = 10;
+ codec->frame_duration_den = 1000;
+ } else {
+ codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
+ codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
+ }
+
+ /* Derive the payload type if it is unknown */
+ if (codec->payload_type == PTYPE_UNDEFINED) {
+
+ /* For the known codecs from the static range we restore
+ * the IANA or 3GPP assigned payload type number */
+ if (codec->rate == 8000 && codec->channels == 1) {
+ /* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
+ if (!strcmp(codec->subtype_name, "GSM"))
+ codec->payload_type = 3;
+ else if (!strcmp(codec->subtype_name, "PCMA"))
+ codec->payload_type = 8;
+ else if (!strcmp(codec->subtype_name, "PCMU"))
+ codec->payload_type = 0;
+ else if (!strcmp(codec->subtype_name, "G729"))
+ codec->payload_type = 18;
+
+ /* See also: 3GPP TS 48.103, chapter 5.4.2.2 RTP Payload
+ * Note: These are not fixed payload types as the IANA
+ * defined once, they still remain dymanic payload
+ * types, but with a payload type number preference. */
+ else if (!strcmp(codec->subtype_name, "GSM-EFR"))
+ codec->payload_type = 110;
+ else if (!strcmp(codec->subtype_name, "GSM-HR-08"))
+ codec->payload_type = 111;
+ else if (!strcmp(codec->subtype_name, "AMR"))
+ codec->payload_type = 112;
+ else if (!strcmp(codec->subtype_name, "AMR-WB"))
+ codec->payload_type = 113;
+ }
+
+ /* If we could not determine a payload type we assume that
+ * we are dealing with a codec from the dynamic range. We
+ * choose a fixed identifier from 96-109. (Note: normally,
+ * the dynamic payload type rante is from 96-127, but from
+ * 110 onwards 3gpp defines prefered codec types, which are
+ * also fixed, see above) */
+ if (codec->payload_type < 0) {
+ codec->payload_type = 96 + pt_offset;
+ if (codec->payload_type > 109)
+ goto error;
+ }
+ }
+
+ return 0;
+error:
+ /* Make sure we leave a clean codec entry on error. */
+ codec_init(codec);
+ memset(codec, 0, sizeof(*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 (e.g. "GSM/8000/1").
+ * \returns 0 on success, -EINVAL on failure. */
+int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name)
+{
+ 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);
+ 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)
+{
+ char codec_name[64];
+
+ /* A codec name must be set, if not, this might mean that the codec
+ * (payload type) that was assigned is unknown to us so we must stop
+ * here. */
+ if (!codec->subtype_name)
+ return false;
+
+ /* We now extract the codec_name (letters before the /, e.g. "GSM"
+ * from the audio name that is stored in the trunk configuration.
+ * We do not compare to the full audio_name because we expect that
+ * "GSM", "GSM/8000" and "GSM/8000/1" are all compatible when the
+ * audio name of the codec is set to "GSM" */
+ if (sscanf(endp->tcfg->audio_name, "%63[^/]/%*d/%*d", codec_name) < 1)
+ return false;
+
+ /* Finally we check if the subtype_name we have generated from the
+ * audio_name in the trunc struct patches the codec_name of the
+ * given codec */
+ if (strcasecmp(codec_name, codec->subtype_name) == 0)
+ return true;
+
+ /* FIXME: It is questinable that the method to pick a compatible
+ * codec can work properly. Since this useses tcfg->audio_name, as
+ * a reference, which is set to "AMR/8000" permanently.
+ * tcfg->audio_name must be updated by the first connection that
+ * has been made on an endpoint, so that the second connection
+ * can make a meaningful decision here */
+
+ return false;
+}
+
+/*! Decide for one suitable codec
+ * \param[in] conn related rtp-connection.
+ * \returns 0 on success, -EINVAL on failure. */
+int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
+{
+ struct mgcp_rtp_end *rtp;
+ unsigned int i;
+ struct mgcp_endpoint *endp;
+ bool codec_assigned = false;
+
+ endp = conn->conn->endp;
+ rtp = &conn->end;
+
+ /* This function works on the results the SDP/LCO parser has extracted
+ * from the MGCP message. The goal is to select a suitable codec for
+ * the given connection. When transcoding is available, the first codec
+ * from the codec list is taken without further checking. When
+ * transcoding is not available, then the choice must be made more
+ * carefully. Each codec in the list is checked until one is found that
+ * is rated compatible. The rating is done by the helper function
+ * is_codec_compatible(), which does the actual checking. */
+ for (i = 0; i < rtp->codecs_assigned; i++) {
+ /* When no transcoding is available, avoid codecs that would
+ * require transcoding. */
+ if (endp->tcfg->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) {
+ LOGP(DLMGCP, LOGL_NOTICE, "transcoding not available, skipping codec: %d/%s\n",
+ rtp->codecs[i].payload_type, rtp->codecs[i].subtype_name);
+ continue;
+ }
+
+ rtp->codec = &rtp->codecs[i];
+ codec_assigned = true;
+ break;
+ }
+
+ /* FIXME: To the reviewes: This is problematic. I do not get why we
+ * need to reset the packet_duration_ms depending on the codec
+ * selection. I thought it were all 20ms? Is this to address some
+ * cornercase. (This piece of code was in the code path before,
+ * together with the note: "TODO/XXX: Store this per codec and derive
+ * it on use" */
+ if (codec_assigned) {
+ if (rtp->maximum_packet_time >= 0
+ && rtp->maximum_packet_time * rtp->codec->frame_duration_den >
+ rtp->codec->frame_duration_num * 1500)
+ rtp->packet_duration_ms = 0;
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
diff --git a/src/libosmo-mgcp/mgcp_conn.c b/src/libosmo-mgcp/mgcp_conn.c
index 998dbc5..3a5db0f 100644
--- a/src/libosmo-mgcp/mgcp_conn.c
+++ b/src/libosmo-mgcp/mgcp_conn.c
@@ -25,9 +25,31 @@
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/mgcp_endp.h>
+#include <osmocom/mgcp/mgcp_sdp.h>
+#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/core/rate_ctr.h>
#include <ctype.h>
+static const struct rate_ctr_desc rate_ctr_desc[] = {
+ [IN_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:in", "Inbound rtp-stream timestamp errors."},
+ [OUT_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:out", "Outbound rtp-stream timestamp errors."},
+ [RTP_PACKETS_RX_CTR] = {"rtp:packets_rx", "Inbound rtp packets."},
+ [RTP_OCTETS_RX_CTR] = {"rtp:octets_rx", "Inbound rtp octets."},
+ [RTP_PACKETS_TX_CTR] = {"rtp:packets_tx", "Outbound rtp packets."},
+ [RTP_OCTETS_TX_CTR] = {"rtp:octets_rx", "Outbound rtp octets."},
+ [RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "dropped rtp packets."}
+};
+
+const static struct rate_ctr_group_desc rate_ctr_group_desc = {
+ .group_name_prefix = "conn_rtp",
+ .group_description = "rtp connection statistics",
+ .class_id = 1,
+ .num_ctr = ARRAY_SIZE(rate_ctr_desc),
+ .ctr_desc = rate_ctr_desc
+};
+
+
/* Allocate a new connection identifier. According to RFC3435, they must
* be unique only within the scope of the endpoint. (Caller must provide
* memory for id) */
@@ -67,26 +89,14 @@ static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
return -1;
}
-/* Reset codec state and free memory */
-static void mgcp_rtp_codec_init(struct mgcp_rtp_codec *codec)
-{
- codec->payload_type = -1;
- codec->subtype_name = NULL;
- codec->audio_name = NULL;
- 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;
-
- /* see also mgcp_sdp.c, mgcp_set_audio_info() */
- talloc_free(codec->subtype_name);
- talloc_free(codec->audio_name);
-}
-
/* Initialize rtp connection struct with default values */
static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn)
{
struct mgcp_rtp_end *end = &conn_rtp->end;
+ /* FIXME: Each new rate counter group requires an unique index. At the
+ * moment we generate this index using this counter, but perhaps there
+ * is a more concious way to assign the indexes. */
+ static unsigned int rate_ctr_index = 0;
conn_rtp->type = MGCP_RTP_DEFAULT;
conn_rtp->osmux.allocated_cid = -1;
@@ -96,7 +106,6 @@ static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn
end->rtp.fd = -1;
end->rtcp.fd = -1;
- memset(&end->stats, 0, sizeof(end->stats));
end->rtp_port = end->rtcp_port = 0;
talloc_free(end->fmtp_extra);
end->fmtp_extra = NULL;
@@ -105,9 +114,15 @@ static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn
end->frames_per_packet = 0; /* unknown */
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
end->output_enabled = 0;
+ end->maximum_packet_time = -1;
+
+ conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index);
+ conn_rtp->state.in_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[IN_STREAM_ERR_TSTMP_CTR];
+ conn_rtp->state.out_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[OUT_STREAM_ERR_TSTMP_CTR];
+ rate_ctr_index++;
- mgcp_rtp_codec_init(&end->codec);
- mgcp_rtp_codec_init(&end->alt_codec);
+ /* Make sure codec table is reset */
+ mgcp_codec_reset_all(conn_rtp);
}
/* Cleanup rtp connection struct */
@@ -116,6 +131,7 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
osmux_disable_conn(conn_rtp);
osmux_release_cid(conn_rtp);
mgcp_free_rtp_port(&conn_rtp->end);
+ rate_ctr_group_free(conn_rtp->rate_ctr_group);
}
/*! allocate a new connection list entry.
diff --git a/src/libosmo-mgcp/mgcp_network.c b/src/libosmo-mgcp/mgcp_network.c
index 6923b97..3ac93be 100644
--- a/src/libosmo-mgcp/mgcp_network.c
+++ b/src/libosmo-mgcp/mgcp_network.c
@@ -222,7 +222,7 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp,
if (seq == sstate->last_seq) {
if (timestamp != sstate->last_timestamp) {
- sstate->err_ts_counter += 1;
+ rate_ctr_inc(sstate->err_ts_ctr);
LOGP(DRTP, LOGL_ERROR,
"The %s timestamp delta is != 0 but the sequence "
"number %d is the same, "
@@ -272,7 +272,7 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp,
ts_alignment_error(sstate, state->packet_duration, timestamp);
if (timestamp_error) {
- sstate->err_ts_counter += 1;
+ rate_ctr_inc(sstate->err_ts_ctr);
LOGP(DRTP, LOGL_NOTICE,
"The %s timestamp has an alignment error of %d "
"on 0x%x SSRC: %u "
@@ -310,7 +310,7 @@ static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp,
ENDPOINT_NUMBER(endp), tsdelta,
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
} else {
- tsdelta = rtp_end->codec.rate * 20 / 1000;
+ tsdelta = rtp_end->codec->rate * 20 / 1000;
LOGP(DRTP, LOGL_NOTICE,
"Fixed packet duration and last timestamp delta "
"are not available on 0x%x, "
@@ -399,12 +399,12 @@ int mgcp_rtp_processing_default(struct mgcp_endpoint *endp,
/*! dummy callback to disable transcoding (see also cfg->setup_rtp_processing_cb).
* \param[in] associated endpoint
- * \param[in] destination RTP end
- * \param[in] source RTP end
+ * \param[in] destination RTP connnection
+ * \param[in] source RTP connection
* \returns ignores input parameters, return always 0 */
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
- struct mgcp_rtp_end *dst_end,
- struct mgcp_rtp_end *src_end)
+ struct mgcp_conn_rtp *conn_dst,
+ struct mgcp_conn_rtp *conn_src)
{
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x transcoding disabled\n",
ENDPOINT_NUMBER(endp));
@@ -421,8 +421,8 @@ void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
"endpoint:0x%x conn:%s using format defaults\n",
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn));
- *payload_type = conn->end.codec.payload_type;
- *audio_name = conn->end.codec.audio_name;
+ *payload_type = conn->end.codec->payload_type;
+ *audio_name = conn->end.codec->audio_name;
*fmtp_extra = conn->end.fmtp_extra;
}
@@ -490,7 +490,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
uint16_t seq;
uint32_t timestamp, ssrc;
struct rtp_hdr *rtp_hdr;
- int payload = rtp_end->codec.payload_type;
+ int payload = rtp_end->codec->payload_type;
if (len < sizeof(*rtp_hdr))
return;
@@ -498,7 +498,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
rtp_hdr = (struct rtp_hdr *)data;
seq = ntohs(rtp_hdr->sequence);
timestamp = ntohl(rtp_hdr->timestamp);
- arrival_time = get_current_ts(rtp_end->codec.rate);
+ arrival_time = get_current_ts(rtp_end->codec->rate);
ssrc = ntohl(rtp_hdr->ssrc);
transit = arrival_time - timestamp;
@@ -511,7 +511,9 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
state->in_stream.last_tsdelta = 0;
state->packet_duration =
mgcp_rtp_packet_duration(endp, rtp_end);
- state->out_stream = state->in_stream;
+ state->out_stream.last_seq = seq - 1;
+ state->out_stream.ssrc = state->patch.orig_ssrc = ssrc;
+ state->out_stream.last_tsdelta = 0;
state->out_stream.last_timestamp = timestamp;
state->out_stream.ssrc = ssrc - 1; /* force output SSRC change */
LOGP(DRTP, LOGL_INFO,
@@ -522,7 +524,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
if (state->packet_duration == 0) {
state->packet_duration =
- rtp_end->codec.rate * 20 / 1000;
+ rtp_end->codec->rate * 20 / 1000;
LOGP(DRTP, LOGL_NOTICE,
"endpoint:0x%x fixed packet duration is not available, "
"using fixed 20ms instead: %d from %s:%d\n",
@@ -678,12 +680,9 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
}
LOGP(DRTP, LOGL_DEBUG,
- "endpoint:0x%x loop:%d, mode:%d ",
- ENDPOINT_NUMBER(endp), tcfg->audio_loop, conn_src->conn->mode);
- if (conn_src->conn->mode == MGCP_CONN_LOOPBACK)
- LOGPC(DRTP, LOGL_DEBUG, "(loopback)\n");
- else
- LOGPC(DRTP, LOGL_DEBUG, "\n");
+ "endpoint:0x%x loop:%d, mode:%d%s\n",
+ ENDPOINT_NUMBER(endp), tcfg->audio_loop, conn_src->conn->mode,
+ conn_src->conn->mode == MGCP_CONN_LOOPBACK ? " (loopback)" : "");
/* Note: In case of loopback configuration, both, the source and the
* destination will point to the same connection. */
@@ -692,7 +691,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
dest_name = conn_dst->conn->name;
if (!rtp_end->output_enabled) {
- rtp_end->stats.dropped_packets += 1;
+ rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_DROPPED_PACKETS_CTR]);
LOGP(DRTP, LOGL_DEBUG,
"endpoint:0x%x output disabled, drop to %s %s "
"rtp_port:%u rtcp_port:%u\n",
@@ -733,11 +732,18 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
* 'e400', or it will reject the RAB assignment. It seems to not harm other femto
* cells (as long as we patch only the first RTP payload in each stream).
*/
- if (!rtp_state->patched_first_rtp_payload) {
+ if (!rtp_state->patched_first_rtp_payload
+ && conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
uint8_t *data = (uint8_t *) & buf[12];
- data[0] = 0xe4;
- data[1] = 0x00;
- rtp_state->patched_first_rtp_payload = true;
+ if (data[0] == 0xe0) {
+ data[0] = 0xe4;
+ data[1] = 0x00;
+ rtp_state->patched_first_rtp_payload = true;
+ LOGP(DRTP, LOGL_DEBUG,
+ "endpoint:0x%x Patching over first two bytes"
+ " to fake an IuUP Initialization Ack\n",
+ ENDPOINT_NUMBER(endp));
+ }
}
len = mgcp_udp_send(rtp_end->rtp.fd,
@@ -747,8 +753,8 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
if (len <= 0)
return len;
- conn_dst->end.stats.packets_tx += 1;
- conn_dst->end.stats.octets_tx += 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);
nbytes += len;
buflen = cont;
@@ -767,8 +773,8 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
&rtp_end->addr,
rtp_end->rtcp_port, buf, len);
- conn_dst->end.stats.packets_tx += 1;
- conn_dst->end.stats.octets_tx += 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);
return len;
}
@@ -865,6 +871,15 @@ static int check_rtp_destin(struct mgcp_conn_rtp *conn)
struct mgcp_endpoint *endp;
endp = conn->conn->endp;
+ /* Note: it is legal to create a connection but never setting a port
+ * and IP-address for outgoing data. */
+ if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0 && conn->end.rtp_port == 0) {
+ LOGP(DRTP, LOGL_DEBUG,
+ "endpoint:0x%x destination IP-address and rtp port is (not yet) known\n",
+ ENDPOINT_NUMBER(endp));
+ return -1;
+ }
+
if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0) {
LOGP(DRTP, LOGL_ERROR,
"endpoint:0x%x destination IP-address is invalid\n",
@@ -928,8 +943,8 @@ static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
}
/* Increment RX statistics */
- conn->end.stats.packets_rx += 1;
- conn->end.stats.octets_rx += rc;
+ 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);
diff --git a/src/libosmo-mgcp/mgcp_osmux.c b/src/libosmo-mgcp/mgcp_osmux.c
index 1d3cab3..26817c8 100644
--- a/src/libosmo-mgcp/mgcp_osmux.c
+++ b/src/libosmo-mgcp/mgcp_osmux.c
@@ -256,8 +256,8 @@ static void scheduled_tx_net_cb(struct msgb *msg, void *data)
.sin_port = conn_net->end.rtp_port,
};
- conn_bts->end.stats.octets_tx += msg->len;
- conn_bts->end.stats.packets_tx++;
+ rate_ctr_inc(&conn_bts->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
+ rate_ctr_add(&conn_bts->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], msg->len);
/* Send RTP data to NET */
/* FIXME: Get rid of conn_bts and conn_net! */
@@ -283,8 +283,8 @@ static void scheduled_tx_bts_cb(struct msgb *msg, void *data)
.sin_port = conn_bts->end.rtp_port,
};
- conn_net->end.stats.octets_tx += msg->len;
- conn_net->end.stats.packets_tx++;
+ rate_ctr_inc(&conn_net->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
+ rate_ctr_add(&conn_net->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], msg->len);
/* Send RTP data to BTS */
/* FIXME: Get rid of conn_bts and conn_net! */
@@ -322,11 +322,10 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
{
struct msgb *msg;
struct osmux_hdr *osmuxh;
- struct llist_head list;
struct sockaddr_in addr;
struct mgcp_config *cfg = ofd->data;
uint32_t rem;
- struct mgcp_conn_rtp *conn_net = NULL;
+ struct mgcp_conn_rtp *conn_bts = NULL;
msg = osmux_recv(ofd, &addr);
if (!msg)
@@ -345,8 +344,8 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
&addr.sin_addr, MGCP_DEST_NET);
/* FIXME: Get rid of CONN_ID_XXX! */
- conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
- if (!conn_net)
+ conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
+ if (!conn_bts)
goto out;
if (!endp) {
@@ -355,12 +354,11 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
osmuxh->circuit_id);
goto out;
}
- conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem);
- conn_net->osmux.stats.chunks++;
+ conn_bts->osmux.stats.octets += osmux_chunk_length(msg, rem);
+ conn_bts->osmux.stats.chunks++;
rem = msg->len;
- osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
- osmux_tx_sched(&list, scheduled_tx_bts_cb, endp);
+ osmux_xfrm_output_sched(&conn_bts->osmux.out, osmuxh);
}
out:
msgb_free(msg);
@@ -426,7 +424,6 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
{
struct msgb *msg;
struct osmux_hdr *osmuxh;
- struct llist_head list;
struct sockaddr_in addr;
struct mgcp_config *cfg = ofd->data;
uint32_t rem;
@@ -463,8 +460,7 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
conn_net->osmux.stats.chunks++;
rem = msg->len;
- osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
- osmux_tx_sched(&list, scheduled_tx_net_cb, endp);
+ osmux_xfrm_output_sched(&conn_net->osmux.out, osmuxh);
}
out:
msgb_free(msg);
@@ -553,9 +549,13 @@ int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
switch (endp->cfg->role) {
case MGCP_BSC_NAT:
conn->type = MGCP_OSMUX_BSC_NAT;
+ osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
+ scheduled_tx_net_cb, endp);
break;
case MGCP_BSC:
conn->type = MGCP_OSMUX_BSC;
+ osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
+ scheduled_tx_bts_cb, endp);
break;
}
@@ -576,6 +576,11 @@ void osmux_disable_conn(struct mgcp_conn_rtp *conn)
LOGP(DLMGCP, LOGL_INFO, "Releasing connection %s using Osmux CID %u\n",
conn->conn->id, conn->osmux.cid);
+
+ /* We are closing, we don't need pending RTP packets to be transmitted */
+ osmux_xfrm_output_set_tx_cb(&conn->osmux.out, NULL, NULL);
+ osmux_xfrm_output_flush(&conn->osmux.out);
+
osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid);
conn->osmux.state = OSMUX_STATE_DISABLED;
conn->osmux.cid = -1;
diff --git a/src/libosmo-mgcp/mgcp_protocol.c b/src/libosmo-mgcp/mgcp_protocol.c
index ea86672..ea80907 100644
--- a/src/libosmo-mgcp/mgcp_protocol.c
+++ b/src/libosmo-mgcp/mgcp_protocol.c
@@ -40,6 +40,7 @@
#include <osmocom/mgcp/mgcp_msg.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_sdp.h>
+#include <osmocom/mgcp/mgcp_codec.h>
struct mgcp_request {
char *name;
@@ -88,8 +89,7 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp,
}
}
- return cfg->setup_rtp_processing_cb(endp, &conn_dst->end,
- &conn_src->end);
+ return cfg->setup_rtp_processing_cb(endp, conn_dst, conn_src);
}
/* array of function pointers for handling various
@@ -356,13 +356,15 @@ static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p)
/* Try to find a free port by attempting to bind on it. Also handle the
* counter that points on the next free port. Since we have a pointer
- * to the next free port, binding should work on the first attempt,
- * nevertheless, try at least the next 200 ports before giving up */
+ * to the next free port, binding should in work on the first attempt in
+ * general. In case of failure the next port is tryed until the whole port
+ * range is tryed once. */
static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
{
int i;
struct mgcp_rtp_end *end;
struct mgcp_port_range *range;
+ unsigned int tries;
OSMO_ASSERT(conn);
end = &conn->end;
@@ -371,7 +373,8 @@ static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
range = &endp->cfg->net_ports;
/* attempt to find a port */
- for (i = 0; i < 200; ++i) {
+ tries = (range->range_end - range->range_start) / 2;
+ for (i = 0; i < tries; ++i) {
int rc;
if (range->last_port >= range->range_end)
@@ -387,8 +390,123 @@ static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
}
LOGP(DLMGCP, LOGL_ERROR,
- "Allocating a RTP/RTCP port failed 200 times 0x%x.\n",
- ENDPOINT_NUMBER(endp));
+ "Allocating a RTP/RTCP port failed %u times 0x%x.\n",
+ tries, ENDPOINT_NUMBER(endp));
+ return -1;
+}
+
+/*! Helper function for check_local_cx_options() to get a pointer of the next
+ * lco option identifier
+ * \param[in] lco string
+ * \returns pointer to the beginning of the LCO identifier, NULL on failure */
+char *get_lco_identifier(const char *options)
+{
+ char *ptr;
+ unsigned int count = 0;
+
+ /* Jump to the end of the lco identifier */
+ ptr = strstr(options, ":");
+ if (!ptr)
+ return NULL;
+
+ /* Walk backwards until the pointer points to the beginning of the
+ * lco identifier. We know that we stand at the beginning when we
+ * are either at the beginning of the memory or see a space or
+ * comma. (this is tolerant, it will accept a:10, b:11 as well as
+ * a:10,b:11) */
+ while (1) {
+ /* Endless loop protection */
+ if (count > 10000)
+ return NULL;
+ else if (ptr < options || *ptr == ' ' || *ptr == ',') {
+ ptr++;
+ break;
+ }
+ ptr--;
+ count++;
+ }
+
+ /* Check if we got any result */
+ if (*ptr == ':')
+ return NULL;
+
+ return ptr;
+}
+
+/*! Check the LCO option. This function checks for multiple appearence of LCO
+ * options, which is illegal
+ * \param[in] ctx talloc context
+ * \param[in] lco string
+ * \returns 0 on success, -1 on failure */
+int check_local_cx_options(void *ctx, const char *options)
+{
+ int i;
+ char *options_copy;
+ char *lco_identifier;
+ char *lco_identifier_end;
+ char *next_lco_identifier;
+
+ char **lco_seen;
+ unsigned int lco_seen_n = 0;
+
+ if (!options)
+ return -1;
+
+ lco_seen =
+ (char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
+ options_copy = talloc_strdup(ctx, options);
+ lco_identifier = options_copy;
+
+ do {
+ /* Move the lco_identifier pointer to the beginning of the
+ * current lco option identifier */
+ lco_identifier = get_lco_identifier(lco_identifier);
+ if (!lco_identifier)
+ goto error;
+
+ /* Look ahead to the next LCO option early, since we
+ * will parse destructively */
+ next_lco_identifier = strstr(lco_identifier + 1, ",");
+
+ /* Pinch off the end of the lco field identifier name
+ * and see if we still got something, also check if
+ * there is some value after the colon. */
+ lco_identifier_end = strstr(lco_identifier, ":");
+ if (!lco_identifier_end)
+ goto error;
+ if (*(lco_identifier_end + 1) == ' '
+ || *(lco_identifier_end + 1) == ','
+ || *(lco_identifier_end + 1) == '\0')
+ goto error;
+ *lco_identifier_end = '\0';
+ if (strlen(lco_identifier) == 0)
+ goto error;
+
+ /* Check if we have already seen the current field identifier
+ * before. If yes, we must bail, an LCO must only appear once
+ * in the LCO string */
+ for (i = 0; i < lco_seen_n; i++) {
+ if (strcmp(lco_seen[i], lco_identifier) == 0)
+ goto error;
+ }
+ lco_seen[lco_seen_n] = lco_identifier;
+ lco_seen_n++;
+
+ /* The first identifier must always be found at the beginnning
+ * of the LCO string */
+ if (lco_seen[0] != options_copy)
+ goto error;
+
+ /* Go to the next lco option */
+ lco_identifier = next_lco_identifier;
+ } while (lco_identifier);
+
+ talloc_free(lco_seen);
+ talloc_free(options_copy);
+ return 0;
+error:
+ talloc_free(lco_seen);
+ talloc_free(options_copy);
return -1;
}
@@ -402,20 +520,34 @@ static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
char *p_opt, *a_opt;
char codec[9];
+ if (!options)
+ return 0;
+ if (strlen(options) == 0)
+ return 0;
+
+ /* Make sure the encoding of the LCO is consistant before we proceed */
+ if (check_local_cx_options(ctx, options) != 0) {
+ LOGP(DLMGCP, LOGL_ERROR,
+ "local CX options: Internal inconsistency in Local Connection Options!\n");
+ return 524;
+ }
+
talloc_free(lco->string);
- talloc_free(lco->codec);
- lco->codec = NULL;
- lco->pkt_period_min = lco->pkt_period_max = 0;
- lco->string = talloc_strdup(ctx, options ? options : "");
+ lco->string = talloc_strdup(ctx, options);
p_opt = strstr(lco->string, "p:");
if (p_opt && sscanf(p_opt, "p:%d-%d",
&lco->pkt_period_min, &lco->pkt_period_max) == 1)
lco->pkt_period_max = lco->pkt_period_min;
+ /* FIXME: LCO also supports the negotiation of more then one codec.
+ * (e.g. a:PCMU;G726-32) But this implementation only supports a single
+ * codec only. */
a_opt = strstr(lco->string, "a:");
- if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1)
+ if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1) {
+ talloc_free(lco->codec);
lco->codec = talloc_strdup(ctx, codec);
+ }
LOGP(DLMGCP, LOGL_DEBUG,
"local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
@@ -456,15 +588,15 @@ uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
/* Get the number of frames per channel and packet */
if (rtp->frames_per_packet)
f = rtp->frames_per_packet;
- else if (rtp->packet_duration_ms && rtp->codec.frame_duration_num) {
- int den = 1000 * rtp->codec.frame_duration_num;
- f = (rtp->packet_duration_ms * rtp->codec.frame_duration_den +
+ else if (rtp->packet_duration_ms && rtp->codec->frame_duration_num) {
+ int den = 1000 * rtp->codec->frame_duration_num;
+ f = (rtp->packet_duration_ms * rtp->codec->frame_duration_den +
den / 2)
/ den;
}
- return rtp->codec.rate * f * rtp->codec.frame_duration_num /
- rtp->codec.frame_duration_den;
+ return rtp->codec->rate * f * rtp->codec->frame_duration_num /
+ rtp->codec->frame_duration_den;
}
static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
@@ -480,6 +612,68 @@ static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
return mgcp_parse_osmux_cid(line);
}
+/* Process codec information contained in CRCX/MDCX */
+static int handle_codec_info(struct mgcp_conn_rtp *conn,
+ struct mgcp_parse_data *p, int have_sdp, bool crcx)
+{
+ struct mgcp_endpoint *endp = p->endp;
+ int rc;
+ char *cmd;
+
+ if (crcx)
+ cmd = "CRCX";
+ else
+ cmd = "MDCX";
+
+ /* Collect codec information */
+ if (have_sdp) {
+ /* If we have SDP, we ignore the local connection options and
+ * use only the SDP information. */
+ mgcp_codec_reset_all(conn);
+ rc = mgcp_parse_sdp_data(endp, conn, p);
+ if (rc != 0) {
+ LOGP(DLMGCP, LOGL_ERROR,
+ "%s: endpoint:%x sdp not parseable\n", cmd,
+ ENDPOINT_NUMBER(endp));
+
+ /* See also RFC 3661: Protocol error */
+ return 510;
+ }
+ } else if (endp->local_options.codec) {
+ /* When no SDP is available, we use the codec information from
+ * the local connection options (if present) */
+ mgcp_codec_reset_all(conn);
+ rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec);
+ if (rc != 0)
+ goto error;
+ }
+
+ /* Make sure we always set a sane default codec */
+ if (conn->end.codecs_assigned == 0) {
+ /* When SDP and/or LCO did not supply any codec information,
+ * than it makes sense to pick a sane default: (payload-type 0,
+ * PCMU), see also: OS#2658 */
+ mgcp_codec_reset_all(conn);
+ rc = mgcp_codec_add(conn, 0, NULL);
+ if (rc != 0)
+ goto error;
+ }
+
+ /* Make codec decision */
+ if (mgcp_codec_decide(conn) != 0)
+ goto error;
+
+ return 0;
+
+error:
+ LOGP(DLMGCP, LOGL_ERROR,
+ "%s: endpoint:0x%x codec negotiation failure\n", cmd,
+ ENDPOINT_NUMBER(endp));
+
+ /* See also RFC 3661: Codec negotiation failure */
+ return 534;
+}
+
/* CRCX command handler, processes the received command */
static struct msgb *handle_create_con(struct mgcp_parse_data *p)
{
@@ -597,17 +791,6 @@ mgcp_header_done:
* connection ids) */
endp->callid = talloc_strdup(tcfg->endpoints, callid);
- /* Extract audio codec information */
- rc = set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
- local_options);
- if (rc != 0) {
- LOGP(DLMGCP, LOGL_ERROR,
- "CRCX: endpoint:%x inavlid local connection options!\n",
- ENDPOINT_NUMBER(endp));
- error_code = rc;
- goto error2;
- }
-
snprintf(conn_name, sizeof(conn_name), "%s", callid);
_conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP, conn_name);
if (!_conn) {
@@ -638,12 +821,27 @@ mgcp_header_done:
goto error2;
}
- /* set up RTP media parameters */
- if (have_sdp)
- mgcp_parse_sdp_data(endp, conn, p);
- else if (endp->local_options.codec)
- mgcp_set_audio_info(p->cfg, &conn->end.codec,
- PTYPE_UNDEFINED, endp->local_options.codec);
+ /* Set local connection options, if present */
+ if (local_options) {
+ rc = set_local_cx_options(endp->tcfg->endpoints,
+ &endp->local_options, local_options);
+ if (rc != 0) {
+ LOGP(DLMGCP, LOGL_ERROR,
+ "CRCX: endpoint:%x inavlid local connection options!\n",
+ ENDPOINT_NUMBER(endp));
+ error_code = rc;
+ goto error2;
+ }
+ }
+
+ /* Handle codec information and decide for a suitable codec */
+ rc = handle_codec_info(conn, p, have_sdp, true);
+ mgcp_codec_summary(conn);
+ if (rc) {
+ error_code = rc;
+ goto error2;
+ }
+
conn->end.fmtp_extra = talloc_strdup(tcfg->endpoints,
tcfg->audio_fmtp_extra);
@@ -718,11 +916,15 @@ mgcp_header_done:
error2:
mgcp_endp_release(endp);
LOGP(DLMGCP, LOGL_NOTICE,
- "CRCX: endpoint:0x%x unable to create connection resource error\n",
+ "CRCX: endpoint:0x%x unable to create connection\n",
ENDPOINT_NUMBER(endp));
return create_err_response(endp, error_code, "CRCX", p->trans);
}
+
+
+
+
/* MDCX command handler, processes the received command */
static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
{
@@ -814,23 +1016,27 @@ mgcp_header_done:
} else
conn->conn->mode = conn->conn->mode_orig;
- if (have_sdp)
- mgcp_parse_sdp_data(endp, conn, p);
+ /* Set local connection options, if present */
+ if (local_options) {
+ rc = set_local_cx_options(endp->tcfg->endpoints,
+ &endp->local_options, local_options);
+ if (rc != 0) {
+ LOGP(DLMGCP, LOGL_ERROR,
+ "MDCX: endpoint:%x inavlid local connection options!\n",
+ ENDPOINT_NUMBER(endp));
+ error_code = rc;
+ goto error3;
+ }
+ }
- rc = set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
- local_options);
- if (rc != 0) {
- LOGP(DLMGCP, LOGL_ERROR,
- "MDCX: endpoint:%x inavlid local connection options!\n",
- ENDPOINT_NUMBER(endp));
+ /* Handle codec information and decide for a suitable codec */
+ rc = handle_codec_info(conn, p, have_sdp, false);
+ mgcp_codec_summary(conn);
+ if (rc) {
error_code = rc;
goto error3;
}
- if (!have_sdp && endp->local_options.codec)
- mgcp_set_audio_info(p->cfg, &conn->end.codec,
- PTYPE_UNDEFINED, endp->local_options.codec);
-
/* check connection mode setting */
if (conn->conn->mode != MGCP_CONN_LOOPBACK
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
@@ -842,6 +1048,7 @@ mgcp_header_done:
goto error3;
}
+
if (setup_rtp_processing(endp, conn) != 0)
goto error3;
diff --git a/src/libosmo-mgcp/mgcp_sdp.c b/src/libosmo-mgcp/mgcp_sdp.c
index 52b4df4..102c8c3 100644
--- a/src/libosmo-mgcp/mgcp_sdp.c
+++ b/src/libosmo-mgcp/mgcp_sdp.c
@@ -25,9 +25,13 @@
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_msg.h>
#include <osmocom/mgcp/mgcp_endp.h>
+#include <osmocom/mgcp/mgcp_codec.h>
#include <errno.h>
+/* A struct to store intermediate parsing results. The function
+ * mgcp_parse_sdp_data() is using it as temporary storage for parsing the SDP
+ * codec information. */
struct sdp_rtp_map {
/* the type */
int payload_type;
@@ -40,89 +44,8 @@ struct sdp_rtp_map {
int channels;
};
-/*! Set codec configuration depending on payload type and codec name.
- * \param[in] ctx talloc context.
- * \param[out] codec configuration (caller provided memory).
- * \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
- * \param[in] audio_name audio codec name (e.g. "GSM/8000/1").
- * \returns 0 on success, -1 on failure. */
-int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
- int payload_type, const char *audio_name)
-{
- int rate = codec->rate;
- int channels = codec->channels;
- char audio_codec[64];
-
- talloc_free(codec->subtype_name);
- codec->subtype_name = NULL;
- talloc_free(codec->audio_name);
- codec->audio_name = NULL;
-
- if (payload_type != PTYPE_UNDEFINED)
- codec->payload_type = payload_type;
-
- if (!audio_name) {
- switch (payload_type) {
- case 0:
- audio_name = "PCMU/8000/1";
- break;
- case 3:
- audio_name = "GSM/8000/1";
- break;
- case 8:
- audio_name = "PCMA/8000/1";
- break;
- case 18:
- audio_name = "G729/8000/1";
- break;
- default:
- /* Payload type is unknown, don't change rate and
- * channels. */
- /* TODO: return value? */
- return 0;
- }
- }
-
- if (sscanf(audio_name, "%63[^/]/%d/%d",
- audio_codec, &rate, &channels) < 1)
- return -EINVAL;
-
- codec->rate = rate;
- codec->channels = channels;
- codec->subtype_name = talloc_strdup(ctx, audio_codec);
- codec->audio_name = talloc_strdup(ctx, audio_name);
-
- if (!strcmp(audio_codec, "G729")) {
- codec->frame_duration_num = 10;
- codec->frame_duration_den = 1000;
- } else {
- codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
- codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
- }
-
- if (payload_type < 0) {
- payload_type = 96;
- if (rate == 8000 && channels == 1) {
- if (!strcmp(audio_codec, "GSM"))
- payload_type = 3;
- else if (!strcmp(audio_codec, "PCMA"))
- payload_type = 8;
- else if (!strcmp(audio_codec, "PCMU"))
- payload_type = 0;
- else if (!strcmp(audio_codec, "G729"))
- payload_type = 18;
- }
-
- codec->payload_type = payload_type;
- }
-
- if (channels != 1)
- LOGP(DLMGCP, LOGL_NOTICE,
- "Channels != 1 in SDP: '%s'\n", audio_name);
-
- return 0;
-}
-
+/* Helper function to extrapolate missing codec parameters in a codec mao from
+ * an already filled in payload_type, called from: mgcp_parse_sdp_data() */
static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
{
int i;
@@ -149,10 +72,16 @@ static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
codecs[i].rate = 8000;
codecs[i].channels = 1;
break;
+ default:
+ codecs[i].codec_name = NULL;
+ codecs[i].rate = 0;
+ codecs[i].channels = 0;
}
}
}
+/* Helper function to update codec map information with additional data from
+ * SDP, called from: mgcp_parse_sdp_data() */
static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
int payload, const char *audio_name)
{
@@ -162,8 +91,13 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
char audio_codec[64];
int rate = -1;
int channels = -1;
+
+ /* Note: We can only update payload codecs that already exist
+ * in our codec list. If we get an unexpected payload type,
+ * we just drop it */
if (codecs[i].payload_type != payload)
continue;
+
if (sscanf(audio_name, "%63[^/]/%d/%d",
audio_codec, &rate, &channels) < 1) {
LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n",
@@ -182,43 +116,72 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
audio_name);
}
-/* Check if the codec matches what is set up in the trunk config */
-static int is_codec_compatible(const struct mgcp_endpoint *endp,
- const struct sdp_rtp_map *codec)
+/* Extract payload types from SDP, also check for duplicates */
+static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs,
+ unsigned int codecs_len, char *sdp)
{
- char *codec_str;
- char audio_codec[64];
+ char *str;
+ char *str_ptr;
+ char *pt_str;
+ unsigned int pt;
+ unsigned int count = 0;
+ unsigned int i;
+
+ str = talloc_zero_size(ctx, strlen(sdp) + 1);
+ str_ptr = str;
+ strcpy(str_ptr, sdp);
+
+ str_ptr = strstr(str_ptr, "RTP/AVP ");
+ if (!str_ptr)
+ goto exit;
+
+ pt_str = strtok(str_ptr, " ");
+ if (!pt_str)
+ goto exit;
+
+ while (1) {
+ /* Do not allow excessive payload types */
+ if (count > codecs_len)
+ goto error;
+
+ pt_str = strtok(NULL, " ");
+ if (!pt_str)
+ break;
- if (!codec->codec_name)
- return 0;
+ pt = atoi(pt_str);
- /* GSM, GSM/8000 and GSM/8000/1 should all be compatible...
- * let's go by name first. */
- codec_str = endp->tcfg->audio_name;
- if (sscanf(codec_str, "%63[^/]/%*d/%*d", audio_codec) < 1)
- return 0;
+ /* Do not allow duplicate payload types */
+ for (i = 0; i < count; i++)
+ if (codecs[i].payload_type == pt)
+ goto error;
+
+ codecs[count].payload_type = pt;
+ count++;
+ }
- return strcasecmp(audio_codec, codec->codec_name) == 0;
+exit:
+ talloc_free(str);
+ return count;
+error:
+ talloc_free(str);
+ return -EINVAL;
}
/*! Analyze SDP input string.
* \param[in] endp trunk endpoint.
* \param[out] conn associated rtp connection.
* \param[out] caller provided memory to store the parsing results.
- * \returns 0 on success, -1 on failure.
*
* Note: In conn (conn->end) the function returns the packet duration,
- * the rtp port and the rtcp port */
+ * rtp port, rtcp port and the codec information.
+ * \returns 0 on success, -1 on failure. */
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
- struct mgcp_conn_rtp *conn,
- struct mgcp_parse_data *p)
+ struct mgcp_conn_rtp *conn, struct mgcp_parse_data *p)
{
- struct sdp_rtp_map codecs[10];
- int codecs_used = 0;
+ struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
+ unsigned int codecs_used = 0;
char *line;
- int maxptime = -1;
- int i;
- int codecs_assigned = 0;
+ unsigned int i;
void *tmp_ctx = talloc_new(NULL);
struct mgcp_rtp_end *rtp;
@@ -255,30 +218,21 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
rtp->packet_duration_ms = 0;
else
rtp->packet_duration_ms = ptime;
- } else if (sscanf(line, "a=maxptime:%d", &ptime2)
- == 1) {
- maxptime = ptime2;
+ } else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
+ rtp->maximum_packet_time = ptime2;
}
break;
case 'm':
- rc = sscanf(line,
- "m=audio %d RTP/AVP %d %d %d %d %d %d %d %d %d %d",
- &port, &codecs[0].payload_type,
- &codecs[1].payload_type,
- &codecs[2].payload_type,
- &codecs[3].payload_type,
- &codecs[4].payload_type,
- &codecs[5].payload_type,
- &codecs[6].payload_type,
- &codecs[7].payload_type,
- &codecs[8].payload_type,
- &codecs[9].payload_type);
- if (rc >= 2) {
+ rc = sscanf(line, "m=audio %d RTP/AVP", &port);
+ if (rc == 1) {
rtp->rtp_port = htons(port);
rtp->rtcp_port = htons(port + 1);
- codecs_used = rc - 1;
- codecs_initialize(tmp_ctx, codecs, codecs_used);
}
+
+ rc = pt_from_sdp(conn->conn, codecs,
+ ARRAY_SIZE(codecs), line);
+ if (rc > 0)
+ codecs_used = rc;
break;
case 'c':
@@ -299,43 +253,37 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
break;
}
}
+ OSMO_ASSERT(codecs_used <= MGCP_MAX_CODECS);
- /* Now select the primary and alt_codec */
- for (i = 0; i < codecs_used && codecs_assigned < 2; ++i) {
- struct mgcp_rtp_codec *codec = codecs_assigned == 0 ?
- &rtp->codec : &rtp->alt_codec;
-
- if (endp->tcfg->no_audio_transcoding &&
- !is_codec_compatible(endp, &codecs[i])) {
- LOGP(DLMGCP, LOGL_NOTICE, "Skipping codec %s\n",
- codecs[i].codec_name);
- continue;
- }
+ /* So far we have only set the payload type in the codec struct. Now we
+ * fill up the remaining fields of the codec description with some default
+ * information */
+ codecs_initialize(tmp_ctx, codecs, codecs_used);
- mgcp_set_audio_info(p->cfg, codec,
- codecs[i].payload_type, codecs[i].map_line);
- codecs_assigned += 1;
+ /* Store parsed codec information */
+ for (i = 0; i < codecs_used; i++) {
+ rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line);
+ if (rc < 0)
+ LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp));
}
- if (codecs_assigned > 0) {
- /* TODO/XXX: Store this per codec and derive it on use */
- if (maxptime >= 0 && maxptime * rtp->codec.frame_duration_den >
- rtp->codec.frame_duration_num * 1500) {
- /* more than 1 frame */
- rtp->packet_duration_ms = 0;
- }
+ talloc_free(tmp_ctx);
- LOGP(DLMGCP, LOGL_NOTICE,
- "Got media info via SDP: port %d, payload %d (%s), "
- "duration %d, addr %s\n",
- ntohs(rtp->rtp_port), rtp->codec.payload_type,
- rtp->codec.subtype_name ? rtp->
- codec.subtype_name : "unknown", rtp->packet_duration_ms,
- inet_ntoa(rtp->addr));
+ LOGP(DLMGCP, LOGL_NOTICE,
+ "Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
+ ntohs(rtp->rtp_port), inet_ntoa(rtp->addr),
+ rtp->packet_duration_ms);
+ if (codecs_used == 0)
+ LOGPC(DLMGCP, LOGL_NOTICE, "none");
+ for (i = 0; i < codecs_used; i++) {
+ LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
+ rtp->codecs[i].payload_type,
+ rtp->codecs[i].subtype_name ? rtp-> codecs[i].subtype_name : "unknown");
+ LOGPC(DLMGCP, LOGL_NOTICE, " ");
}
+ LOGPC(DLMGCP, LOGL_NOTICE, "\n");
- talloc_free(tmp_ctx);
- return codecs_assigned > 0;
+ return 0;
}
/*! Generate SDP response string.
@@ -380,7 +328,9 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
if (rc < 0)
goto buffer_too_small;
- if (audio_name && endp->tcfg->audio_send_name) {
+ /* FIXME: Check if the payload type is from the static range,
+ * if yes, omitthe a=rtpmap since it is unnecessary */
+ if (audio_name && endp->tcfg->audio_send_name && (payload_type >= 96 && payload_type <= 127)) {
rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n",
payload_type, audio_name);
diff --git a/src/libosmo-mgcp/mgcp_stat.c b/src/libosmo-mgcp/mgcp_stat.c
index 581130c..3685cfe 100644
--- a/src/libosmo-mgcp/mgcp_stat.c
+++ b/src/libosmo-mgcp/mgcp_stat.c
@@ -25,12 +25,14 @@
#include <osmocom/mgcp/mgcp_stat.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <limits.h>
+#include <inttypes.h>
/* Helper function for mgcp_format_stats_rtp() to calculate packet loss */
-void calc_loss(struct mgcp_rtp_state *state,
- struct mgcp_rtp_end *end, uint32_t *expected,
- int *loss)
+void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss)
{
+ struct mgcp_rtp_state *state = &conn->state;
+ struct rate_ctr *packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
+
*expected = state->stats.cycles + state->stats.max_seq;
*expected = *expected - state->stats.base_seq + 1;
@@ -44,8 +46,8 @@ void calc_loss(struct mgcp_rtp_state *state,
* Make sure the sign is correct and use the biggest
* positive/negative number that fits.
*/
- *loss = *expected - end->stats.packets_rx;
- if (*expected < end->stats.packets_rx) {
+ *loss = *expected - packets_rx->current;
+ if (*expected < packets_rx->current) {
if (*loss > 0)
*loss = INT_MIN;
} else {
@@ -70,13 +72,18 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
int ploss;
int nchars;
- calc_loss(&conn->state, &conn->end, &expected, &ploss);
+ struct rate_ctr *packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
+ struct rate_ctr *octets_rx = &conn->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR];
+ struct rate_ctr *packets_tx = &conn->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR];
+ struct rate_ctr *octets_tx = &conn->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR];
+
+ calc_loss(conn, &expected, &ploss);
jitter = calc_jitter(&conn->state);
nchars = snprintf(str, str_len,
- "\r\nP: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
- conn->end.stats.packets_tx, conn->end.stats.octets_tx,
- conn->end.stats.packets_rx, conn->end.stats.octets_rx,
+ "\r\nP: PS=%" PRIu64 ", OS=%" PRIu64 ", PR=%" PRIu64 ", OR=%" PRIu64 ", PL=%d, JI=%u",
+ packets_tx->current, octets_tx->current,
+ packets_rx->current, octets_rx->current,
ploss, jitter);
if (nchars < 0 || nchars >= str_len)
goto truncate;
@@ -87,9 +94,9 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
if (conn->conn->endp->cfg->osmux != OSMUX_USAGE_OFF) {
/* Error Counter */
nchars = snprintf(str, str_len,
- "\r\nX-Osmo-CP: EC TI=%u, TO=%u",
- conn->state.in_stream.err_ts_counter,
- conn->state.out_stream.err_ts_counter);
+ "\r\nX-Osmo-CP: EC TI=%" PRIu64 ", TO=%" PRIu64,
+ conn->state.in_stream.err_ts_ctr->current,
+ conn->state.out_stream.err_ts_ctr->current);
if (nchars < 0 || nchars >= str_len)
goto truncate;
diff --git a/src/libosmo-mgcp/mgcp_vty.c b/src/libosmo-mgcp/mgcp_vty.c
index 14ecd17..ad462b7 100644
--- a/src/libosmo-mgcp/mgcp_vty.c
+++ b/src/libosmo-mgcp/mgcp_vty.c
@@ -30,6 +30,7 @@
#include <osmocom/mgcp/mgcp_endp.h>
#include <string.h>
+#include <inttypes.h>
#define RTCP_OMIT_STR "Drop RTCP packets in both directions\n"
#define RTP_PATCH_STR "Modify RTP packet header in both directions\n"
@@ -154,22 +155,27 @@ static int config_write_mgcp(struct vty *vty)
return CMD_SUCCESS;
}
-static void dump_rtp_end(struct vty *vty, struct mgcp_rtp_state *state,
- struct mgcp_rtp_end *end)
+static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
{
- struct mgcp_rtp_codec *codec = &end->codec;
+ struct mgcp_rtp_state *state = &conn->state;
+ struct mgcp_rtp_end *end = &conn->end;
+ struct mgcp_rtp_codec *codec = end->codec;
+ struct rate_ctr *dropped_packets;
+
+ dropped_packets = &conn->rate_ctr_group->ctr[RTP_DROPPED_PACKETS_CTR];
vty_out(vty,
- " Timestamp Errs: %d->%d%s"
- " Dropped Packets: %d%s"
+ " Timestamp Errs: %" PRIu64 "->%" PRIu64 "%s"
+ " Dropped Packets: %" PRIu64 "%s"
" Payload Type: %d Rate: %u Channels: %d %s"
" Frame Duration: %u Frame Denominator: %u%s"
" FPP: %d Packet Duration: %u%s"
" FMTP-Extra: %s Audio-Name: %s Sub-Type: %s%s"
" Output-Enabled: %d Force-PTIME: %d%s",
- state->in_stream.err_ts_counter,
- state->out_stream.err_ts_counter, VTY_NEWLINE,
- end->stats.dropped_packets, VTY_NEWLINE,
+ state->in_stream.err_ts_ctr->current,
+ state->out_stream.err_ts_ctr->current,
+ VTY_NEWLINE,
+ dropped_packets->current, VTY_NEWLINE,
codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE,
codec->frame_duration_num, codec->frame_duration_den,
VTY_NEWLINE, end->frames_per_packet, end->packet_duration_ms,
@@ -207,8 +213,7 @@ static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg,
* connection types (E1) as soon as
* the implementation is available */
if (conn->type == MGCP_CONN_TYPE_RTP) {
- dump_rtp_end(vty, &conn->u.rtp.state,
- &conn->u.rtp.end);
+ dump_rtp_end(vty, &conn->u.rtp);
}
}
}
@@ -281,13 +286,6 @@ DEFUN(cfg_mgcp_bind_early,
return CMD_WARNING;
}
-static void parse_range(struct mgcp_port_range *range, const char **argv)
-{
- range->range_start = atoi(argv[0]);
- range->range_end = atoi(argv[1]);
- range->last_port = g_cfg->net_ports.range_start;
-}
-
#define RTP_STR "RTP configuration\n"
#define UDP_PORT_STR "UDP Port number\n"
#define NET_START_STR "First UDP port allocated\n"
@@ -296,11 +294,38 @@ static void parse_range(struct mgcp_port_range *range, const char **argv)
DEFUN(cfg_mgcp_rtp_port_range,
cfg_mgcp_rtp_port_range_cmd,
- "rtp port-range <0-65534> <0-65534>",
+ "rtp port-range <1024-65534> <1025-65535>",
RTP_STR "Range of ports to use for the NET side\n"
RANGE_START_STR RANGE_END_STR)
{
- parse_range(&g_cfg->net_ports, argv);
+ int start;
+ int end;
+
+ start = atoi(argv[0]);
+ end = atoi(argv[1]);
+
+ if (end < start) {
+ vty_out(vty, "range end port (%i) must be greater than the range start port (%i)!%s",
+ end, start, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (start & 1) {
+ vty_out(vty, "range must begin at an even port number, autocorrecting port (%i) to: %i%s",
+ start, start & 0xFFFE, VTY_NEWLINE);
+ start &= 0xFFFE;
+ }
+
+ if ((end & 1) == 0) {
+ vty_out(vty, "range must end at an odd port number, autocorrecting port (%i) to: %i%s",
+ end, end | 1, VTY_NEWLINE);
+ end |= 1;
+ }
+
+ g_cfg->net_ports.range_start = start;
+ g_cfg->net_ports.range_end = end;
+ g_cfg->net_ports.last_port = g_cfg->net_ports.range_start;
+
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgcp_rtp_port_range,
diff --git a/tests/mgcp/mgcp_test.c b/tests/mgcp/mgcp_test.c
index f6c421a..56d0cee 100644
--- a/tests/mgcp/mgcp_test.c
+++ b/tests/mgcp/mgcp_test.c
@@ -26,6 +26,8 @@
#include <osmocom/mgcp/mgcp_stat.h>
#include <osmocom/mgcp/mgcp_msg.h>
#include <osmocom/mgcp/mgcp_endp.h>
+#include <osmocom/mgcp/mgcp_sdp.h>
+#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>
@@ -156,8 +158,8 @@ static void test_strline(void)
"s=-\r\n" \
"c=IN IP4 0.0.0.0\r\n" \
"t=0 0\r\n" \
- "m=audio 16002 RTP/AVP 96\r\n" \
- "a=rtpmap:96 AMR\r\n" \
+ "m=audio 16002 RTP/AVP 112\r\n" \
+ "a=rtpmap:112 AMR\r\n" \
"a=ptime:40\r\n"
#define MDCX4_PT1 \
@@ -404,7 +406,7 @@ static void test_strline(void)
"v=0\r\n" \
"o=- 1439038275 1439038275 IN IP4 192.168.181.247\r\n" \
"s=-\r\nc=IN IP4 192.168.181.247\r\n" \
- "t=0 0\r\nm=audio 29084 RTP/AVP 255 0 8 3 18 4 96 97 101\r\n" \
+ "t=0 0\r\nm=audio 29084 RTP/AVP 0 8 3 18 4 96 97 101\r\n" \
"a=rtpmap:0 PCMU/8000\r\n" \
"a=rtpmap:8 PCMA/8000\r\n" \
"a=rtpmap:3 gsm/8000\r\n" \
@@ -425,7 +427,24 @@ static void test_strline(void)
"I: %s\r\n" \
"\r\n" \
"c=IN IP4 8.8.8.8\r\n" \
- "m=audio 16434 RTP/AVP 255\r\n"
+ "m=audio 16434 RTP/AVP 3\r\n"
+
+#define CRCX_NO_LCO_NO_SDP \
+ "CRCX 2 6@mgw MGCP 1.0\r\n" \
+ "M: recvonly\r\n" \
+ "C: 2\r\n"
+
+#define CRCX_NO_LCO_NO_SDP_RET \
+ "200 2 OK\r\n" \
+ "I: %s\r\n" \
+ "\r\n" \
+ "v=0\r\n" \
+ "o=- %s 23 IN IP4 0.0.0.0\r\n" \
+ "s=-\r\n" \
+ "c=IN IP4 0.0.0.0\r\n" \
+ "t=0 0\r\n" \
+ "m=audio 16008 RTP/AVP 0\r\n" \
+ "a=ptime:20\r\n"
struct mgcp_test {
const char *name;
@@ -462,6 +481,7 @@ static const struct mgcp_test tests[] = {
{"MDCX3", MDCX3, MDCX3_FMTP_RET, PTYPE_NONE,.extra_fmtp =
"a=fmtp:126 0/1/2"},
{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE,.extra_fmtp = "a=fmtp:126 0/1/2"},
+ {"CRCX", CRCX_NO_LCO_NO_SDP, CRCX_NO_LCO_NO_SDP_RET, 97},
};
static const struct mgcp_test retransmit[] = {
@@ -764,14 +784,14 @@ static void test_messages(void)
fprintf(stderr, "endpoint %d: "
"payload type %d (expected %d)\n",
last_endpoint,
- conn->end.codec.payload_type, t->ptype);
+ conn->end.codec->payload_type, t->ptype);
if (t->ptype != PTYPE_IGNORE)
- OSMO_ASSERT(conn->end.codec.payload_type ==
+ OSMO_ASSERT(conn->end.codec->payload_type ==
t->ptype);
/* Reset them again for next test */
- conn->end.codec.payload_type = PTYPE_NONE;
+ conn->end.codec->payload_type = PTYPE_NONE;
}
}
@@ -917,23 +937,43 @@ static const struct pl_test pl_test_dat[] = {
static void test_packet_loss_calc(void)
{
int i;
+ struct mgcp_endpoint endp;
+ struct mgcp_trunk_config trunk;
+
printf("Testing packet loss calculation.\n");
+ memset(&endp, 0, sizeof(endp));
+ memset(&trunk, 0, sizeof(trunk));
+
+ endp.type = &ep_typeset.rtp;
+ trunk.vty_number_endpoints = 1;
+ trunk.endpoints = &endp;
+ endp.tcfg = &trunk;
+ INIT_LLIST_HEAD(&endp.conns);
+
for (i = 0; i < ARRAY_SIZE(pl_test_dat); ++i) {
uint32_t expected;
int loss;
- struct mgcp_rtp_state state;
- struct mgcp_rtp_end rtp;
- memset(&state, 0, sizeof(state));
- memset(&rtp, 0, sizeof(rtp));
- state.stats.initialized = 1;
- state.stats.base_seq = pl_test_dat[i].base_seq;
- state.stats.max_seq = pl_test_dat[i].max_seq;
- state.stats.cycles = pl_test_dat[i].cycles;
+ struct mgcp_conn_rtp *conn = NULL;
+ struct mgcp_conn *_conn = NULL;
+ struct mgcp_rtp_state *state;
+ struct rate_ctr *packets_rx;
- rtp.stats.packets_rx = pl_test_dat[i].packets;
- calc_loss(&state, &rtp, &expected, &loss);
+ _conn =
+ mgcp_conn_alloc(NULL, &endp, MGCP_CONN_TYPE_RTP,
+ "test-connection");
+ conn = mgcp_conn_get_rtp(&endp, _conn->id);
+ state = &conn->state;
+ packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
+
+ state->stats.initialized = 1;
+ state->stats.base_seq = pl_test_dat[i].base_seq;
+ state->stats.max_seq = pl_test_dat[i].max_seq;
+ state->stats.cycles = pl_test_dat[i].cycles;
+
+ packets_rx->current = pl_test_dat[i].packets;
+ calc_loss(conn, &expected, &loss);
if (loss != pl_test_dat[i].loss
|| expected != pl_test_dat[i].expected) {
@@ -942,7 +982,10 @@ static void test_packet_loss_calc(void)
i, loss, pl_test_dat[i].loss, expected,
pl_test_dat[i].expected);
}
+
+ mgcp_conn_free_all(&endp);
}
+
}
int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os,
@@ -1129,10 +1172,12 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
uint32_t last_ssrc = 0;
uint32_t last_timestamp = 0;
uint32_t last_seqno = 0;
- int last_in_ts_err_cnt = 0;
- int last_out_ts_err_cnt = 0;
+ uint64_t last_in_ts_err_cnt = 0;
+ uint64_t last_out_ts_err_cnt = 0;
struct mgcp_conn_rtp *conn = NULL;
struct mgcp_conn *_conn = NULL;
+ struct rate_ctr test_ctr_in;
+ struct rate_ctr test_ctr_out;
printf("Testing packet error detection%s%s.\n",
patch_ssrc ? ", patch SSRC" : "",
@@ -1142,6 +1187,11 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
memset(&endp, 0, sizeof(endp));
memset(&state, 0, sizeof(state));
+ memset(&test_ctr_in, 0, sizeof(test_ctr_in));
+ memset(&test_ctr_out, 0, sizeof(test_ctr_out));
+ state.in_stream.err_ts_ctr = &test_ctr_in;
+ state.out_stream.err_ts_ctr = &test_ctr_out;
+
endp.type = &ep_typeset.rtp;
trunk.vty_number_endpoints = 1;
@@ -1160,7 +1210,8 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
rtp = &conn->end;
- rtp->codec.payload_type = 98;
+ OSMO_ASSERT(mgcp_codec_add(conn, PTYPE_UNDEFINED, "AMR/8000/1") == 0);
+ rtp->codec = &rtp->codecs[0];
for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
struct rtp_packet_info *info = test_rtp_packets1 + i;
@@ -1186,18 +1237,18 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
state.in_stream.last_tsdelta, state.in_stream.last_seq);
printf("Out TS change: %d, dTS: %d, Seq change: %d, "
- "TS Err change: in %+d, out %+d\n",
+ "TS Err change: in +%u, out +%u\n",
state.out_stream.last_timestamp - last_timestamp,
state.out_stream.last_tsdelta,
state.out_stream.last_seq - last_seqno,
- state.in_stream.err_ts_counter - last_in_ts_err_cnt,
- state.out_stream.err_ts_counter - last_out_ts_err_cnt);
+ (unsigned int) (state.in_stream.err_ts_ctr->current - last_in_ts_err_cnt),
+ (unsigned int) (state.out_stream.err_ts_ctr->current - last_out_ts_err_cnt));
printf("Stats: Jitter = %u, Transit = %d\n",
calc_jitter(&state), state.stats.transit);
- last_in_ts_err_cnt = state.in_stream.err_ts_counter;
- last_out_ts_err_cnt = state.out_stream.err_ts_counter;
+ last_in_ts_err_cnt = state.in_stream.err_ts_ctr->current;
+ last_out_ts_err_cnt = state.out_stream.err_ts_ctr->current;
last_timestamp = state.out_stream.last_timestamp;
last_seqno = state.out_stream.last_seq;
}
@@ -1236,8 +1287,7 @@ static void test_multilple_codec(void)
endp = &cfg->trunk.endpoints[last_endpoint];
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
- OSMO_ASSERT(conn->end.codec.payload_type == 18);
- OSMO_ASSERT(conn->end.alt_codec.payload_type == 97);
+ OSMO_ASSERT(conn->end.codec->payload_type == 18);
/* Allocate 2@mgw with three codecs, last one ignored */
last_endpoint = -1;
@@ -1252,10 +1302,14 @@ static void test_multilple_codec(void)
endp = &cfg->trunk.endpoints[last_endpoint];
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
- OSMO_ASSERT(conn->end.codec.payload_type == 18);
- OSMO_ASSERT(conn->end.alt_codec.payload_type == 97);
-
- /* Allocate 3@mgw with no codecs, check for PT == -1 */
+ OSMO_ASSERT(conn->end.codec->payload_type == 18);
+
+ /* Allocate 3@mgw with no codecs, check for PT == 0 */
+ /* Note: It usually makes no sense to leave the payload type list
+ * out. However RFC 2327 does not clearly forbid this case and
+ * it makes and since we already decided in OS#2658 that a missing
+ * LCO should pick a sane default codec, it makes sense to expect
+ * the same behaviour if SDP lacks proper payload type information */
last_endpoint = -1;
inp = create_msg(CRCX_MULT_3, NULL);
resp = mgcp_handle_message(cfg, inp);
@@ -1268,8 +1322,7 @@ static void test_multilple_codec(void)
endp = &cfg->trunk.endpoints[last_endpoint];
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
- OSMO_ASSERT(conn->end.codec.payload_type == -1);
- OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
+ OSMO_ASSERT(conn->end.codec->payload_type == 0);
/* Allocate 4@mgw with a single codec */
last_endpoint = -1;
@@ -1284,8 +1337,7 @@ static void test_multilple_codec(void)
endp = &cfg->trunk.endpoints[last_endpoint];
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
- OSMO_ASSERT(conn->end.codec.payload_type == 18);
- OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
+ OSMO_ASSERT(conn->end.codec->payload_type == 18);
/* Allocate 5@mgw at select GSM.. */
last_endpoint = -1;
@@ -1303,8 +1355,7 @@ static void test_multilple_codec(void)
endp = &cfg->trunk.endpoints[last_endpoint];
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
- OSMO_ASSERT(conn->end.codec.payload_type == 3);
- OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
+ OSMO_ASSERT(conn->end.codec->payload_type == 3);
inp = create_msg(MDCX_NAT_DUMMY, conn_id);
last_endpoint = -1;
@@ -1315,8 +1366,7 @@ static void test_multilple_codec(void)
endp = &cfg->trunk.endpoints[last_endpoint];
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
- OSMO_ASSERT(conn->end.codec.payload_type == 3);
- OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
+ OSMO_ASSERT(conn->end.codec->payload_type == 3);
OSMO_ASSERT(conn->end.rtp_port == htons(16434));
memset(&addr, 0, sizeof(addr));
inet_aton("8.8.8.8", &addr);
@@ -1346,8 +1396,7 @@ static void test_multilple_codec(void)
endp = &cfg->trunk.endpoints[last_endpoint];
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
- OSMO_ASSERT(conn->end.codec.payload_type == 255);
- OSMO_ASSERT(conn->end.alt_codec.payload_type == 0);
+ OSMO_ASSERT(conn->end.codec->payload_type == 0);
talloc_free(cfg);
}
@@ -1465,6 +1514,108 @@ const struct log_info log_info = {
.num_cat = ARRAY_SIZE(log_categories),
};
+static void test_get_lco_identifier(void)
+{
+ char *test;
+ printf("Testing get_lco_identifier()\n");
+
+ /* Normal case at the beginning */
+ test = "p:10, a:PCMU";
+ printf("%s -> %s\n", test, get_lco_identifier(test));
+
+ test = "p:10, a:PCMU";
+ printf("%s -> %s\n", test, get_lco_identifier(test));
+
+ /* Begin parsing in the middle of the value part of
+ * the previous LCO option value */
+ test = "XXXX, p:10, a:PCMU";
+ printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
+
+ test = "XXXX,p:10,a:PCMU";
+ printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
+
+ test = "10,a:PCMU";
+ printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
+
+ test = "10, a:PCMU";
+ printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
+
+ test = "10,a: PCMU";
+ printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
+
+ test = "10 ,a: PCMU";
+ printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
+
+ /* Begin parsing right at the end of the previous LCO
+ * option value */
+ test = ", a:PCMU";
+ printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
+
+ test = " a:PCMU";
+ printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
+
+ /* Empty string, result should be (null) */
+ test = "";
+ printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
+
+ /* Missing colons, result should be (null) */
+ test = "p10, aPCMU";
+ printf("%s -> %s\n", test, get_lco_identifier(test));
+
+ /* Colon separated from the identifier, result should be (null) */
+ test = "10,a :PCMU";
+ printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
+}
+
+static void test_check_local_cx_options(void *ctx)
+{
+ /* Legal cases */
+ OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU") == 0);
+ OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU") == 0);
+ OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, p:10, IN:10") == 0);
+ OSMO_ASSERT(check_local_cx_options(ctx, "one:AAA, two:BB, tree:C") ==
+ 0);
+ OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU") == 0);
+ OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:G726-32") == 0);
+ OSMO_ASSERT(check_local_cx_options(ctx, "p:10-20, b:64") == 0);
+ OSMO_ASSERT(check_local_cx_options(ctx, "b:32-64, e:off") == 0);
+ OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU;G726-32") == 0);
+
+ /* Illegal spaces before and after colon */
+ OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, p :10") == -1);
+ OSMO_ASSERT(check_local_cx_options(ctx, "a :PCMU, p:10") == -1);
+ OSMO_ASSERT(check_local_cx_options(ctx, "p: 10, a:PCMU") == -1);
+
+ /* Check if multiple appearances of LCOs are rejected */
+ OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU, p:10") == -1);
+ OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU, a:PCMU, p:10") ==
+ -1);
+ OSMO_ASSERT(check_local_cx_options(ctx, "p:10, p:10") == -1);
+
+ /* Check if empty LCO are rejected */
+ OSMO_ASSERT(check_local_cx_options(ctx, "p: , a:PCMU") == -1);
+ OSMO_ASSERT(check_local_cx_options(ctx, "p: , a: PCMU") == -1);
+ OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a: PCMU") == -1);
+ OSMO_ASSERT(check_local_cx_options(ctx, "p:, a:PCMU") == -1);
+ OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:") == -1);
+
+ /* Garbeled beginning and ends */
+ OSMO_ASSERT(check_local_cx_options(ctx, ":10, a:10") == -1);
+ OSMO_ASSERT(check_local_cx_options(ctx, "10, a:PCMU") == -1);
+ OSMO_ASSERT(check_local_cx_options(ctx, ", a:PCMU") == -1);
+ OSMO_ASSERT(check_local_cx_options(ctx, " a:PCMU") == -1);
+ OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU,") == -1);
+ OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, ") == -1);
+
+ /* Illegal strings */
+ OSMO_ASSERT(check_local_cx_options(ctx, " ") == -1);
+ OSMO_ASSERT(check_local_cx_options(ctx, "") == -1);
+ OSMO_ASSERT(check_local_cx_options(ctx, "AAA") == -1);
+ OSMO_ASSERT(check_local_cx_options(ctx, ":,") == -1);
+ OSMO_ASSERT(check_local_cx_options(ctx, ",:") == -1);
+ OSMO_ASSERT(check_local_cx_options(ctx, ",,,") == -1);
+}
+
int main(int argc, char **argv)
{
void *ctx = talloc_named_const(NULL, 0, "mgcp_test");
@@ -1486,6 +1637,8 @@ int main(int argc, char **argv)
test_no_cycle();
test_no_name();
test_osmux_cid();
+ test_get_lco_identifier();
+ test_check_local_cx_options(ctx);
OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0);
OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1);
diff --git a/tests/mgcp/mgcp_test.ok b/tests/mgcp/mgcp_test.ok
index d2879ad..9838f4d 100644
--- a/tests/mgcp/mgcp_test.ok
+++ b/tests/mgcp/mgcp_test.ok
@@ -408,6 +408,21 @@ Response matches our expectations.
Testing CRCX
creating message from statically defined input:
---------8<---------
+CRCX 2 6@mgw MGCP 1.0
+M: recvonly
+C: 2
+
+---------8<---------
+checking response:
+using message with patched conn_id for comparison
+Response matches our expectations.
+(response does not contain a connection id)
+Dummy packets: 2
+
+================================================
+Testing CRCX
+creating message from statically defined input:
+---------8<---------
CRCX 2 1@mgw MGCP 1.0
M: recvonly
C: 2
@@ -1031,7 +1046,7 @@ o=- 1439038275 1439038275 IN IP4 192.168.181.247
s=-
c=IN IP4 192.168.181.247
t=0 0
-m=audio 29084 RTP/AVP 255 0 8 3 18 4 96 97 101
+m=audio 29084 RTP/AVP 0 8 3 18 4 96 97 101
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:3 gsm/8000
@@ -1054,7 +1069,7 @@ C: 1355c6041e
I: %s
c=IN IP4 8.8.8.8
-m=audio 16434 RTP/AVP 255
+m=audio 16434 RTP/AVP 3
---------8<---------
creating message from statically defined input:
@@ -1069,7 +1084,7 @@ o=- 1439038275 1439038275 IN IP4 192.168.181.247
s=-
c=IN IP4 192.168.181.247
t=0 0
-m=audio 29084 RTP/AVP 255 0 8 3 18 4 96 97 101
+m=audio 29084 RTP/AVP 0 8 3 18 4 96 97 101
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:3 gsm/8000
@@ -1104,4 +1119,18 @@ a=ptime:40
checking response:
using message with patched conn_id for comparison
Response matches our expectations.
+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)'
Done
diff --git a/tests/mgcp_client/mgcp_client_test.c b/tests/mgcp_client/mgcp_client_test.c
index 007b90c..9978f79 100644
--- a/tests/mgcp_client/mgcp_client_test.c
+++ b/tests/mgcp_client/mgcp_client_test.c
@@ -95,21 +95,26 @@ static int reply_to(mgcp_trans_id_t trans_id, int code, const char *comment,
void test_response_cb(struct mgcp_response *response, void *priv)
{
+ unsigned int i;
OSMO_ASSERT(priv == mgcp);
mgcp_response_parse_params(response);
- printf("response cb received:\n"
- " head.response_code = %d\n"
- " head.trans_id = %u\n"
- " head.comment = %s\n"
- " audio_port = %u\n"
- " audio_ip = %s\n",
- response->head.response_code,
- response->head.trans_id,
- response->head.comment,
- response->audio_port,
- response->audio_ip
- );
+ printf("response cb received:\n");
+ printf(" head.response_code = %d\n", response->head.response_code);
+ printf(" head.trans_id = %u\n", response->head.trans_id);
+ printf(" head.comment = %s\n", response->head.comment);
+ printf(" audio_port = %u\n", response->audio_port);
+ printf(" audio_ip = %s\n", response->audio_ip);
+ printf(" ptime = %u\n", response->ptime);
+ printf(" codecs_len = %u\n", response->codecs_len);
+ for(i=0;i<response->codecs_len;i++)
+ printf(" codecs[%u] = %u\n", i, response->codecs[i]);
+ printf(" ptmap_len = %u\n", response->ptmap_len);
+ for(i=0;i<response->ptmap_len;i++) {
+ printf(" ptmap[%u].codec = %u\n", i, response->ptmap[i].codec);
+ printf(" ptmap[%u].pt = %u\n", i, response->ptmap[i].pt);
+ }
+
}
mgcp_trans_id_t dummy_mgcp_send(struct msgb *msg)
@@ -149,8 +154,9 @@ void test_crcx(void)
"s=-\r\n"
"c=IN IP4 10.9.1.120\r\n"
"t=0 0\r\n"
- "m=audio 16002 RTP/AVP 98\r\n"
- "a=rtpmap:98 AMR/8000\r\n"
+ "m=audio 16002 RTP/AVP 110 96\r\n"
+ "a=rtpmap:110 AMR/8000\r\n"
+ "a=rtpmap:96 GSM-EFR/8000\r\n"
"a=ptime:20\r\n");
}
@@ -166,7 +172,15 @@ void test_mgcp_msg(void)
.audio_port = 1234,
.call_id = 47,
.conn_id = "11",
- .conn_mode = MGCP_CONN_RECV_SEND
+ .conn_mode = MGCP_CONN_RECV_SEND,
+ .ptime = 20,
+ .codecs[0] = CODEC_GSM_8000_1,
+ .codecs[1] = CODEC_AMR_8000_1,
+ .codecs[2] = CODEC_GSMEFR_8000_1,
+ .codecs_len = 1,
+ .ptmap[0].codec = CODEC_GSMEFR_8000_1,
+ .ptmap[0].pt = 96,
+ .ptmap_len = 1
};
if (mgcp)
@@ -183,6 +197,26 @@ void test_mgcp_msg(void)
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
printf("%s\n", (char *)msg->data);
+ printf("Generated CRCX message (two codecs):\n");
+ mgcp_msg.verb = MGCP_VERB_CRCX;
+ mgcp_msg.presence =
+ (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
+ MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
+ mgcp_msg.codecs_len = 2;
+ msg = mgcp_msg_gen(mgcp, &mgcp_msg);
+ mgcp_msg.codecs_len = 1;
+ printf("%s\n", (char *)msg->data);
+
+ printf("Generated CRCX message (three codecs, one with custom pt):\n");
+ mgcp_msg.verb = MGCP_VERB_CRCX;
+ mgcp_msg.presence =
+ (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
+ MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
+ mgcp_msg.codecs_len = 3;
+ msg = mgcp_msg_gen(mgcp, &mgcp_msg);
+ mgcp_msg.codecs_len = 1;
+ printf("%s\n", (char *)msg->data);
+
printf("Generated MDCX message:\n");
mgcp_msg.verb = MGCP_VERB_MDCX;
mgcp_msg.presence =
@@ -192,6 +226,28 @@ void test_mgcp_msg(void)
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
printf("%s\n", (char *)msg->data);
+ printf("Generated MDCX message (two codecs):\n");
+ mgcp_msg.verb = MGCP_VERB_MDCX;
+ mgcp_msg.presence =
+ (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
+ MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE |
+ MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
+ mgcp_msg.codecs_len = 2;
+ msg = mgcp_msg_gen(mgcp, &mgcp_msg);
+ mgcp_msg.codecs_len = 1;
+ printf("%s\n", (char *)msg->data);
+
+ printf("Generated MDCX message (three codecs, one with custom pt):\n");
+ mgcp_msg.verb = MGCP_VERB_MDCX;
+ mgcp_msg.presence =
+ (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
+ MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE |
+ MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
+ mgcp_msg.codecs_len = 3;
+ msg = mgcp_msg_gen(mgcp, &mgcp_msg);
+ mgcp_msg.codecs_len = 1;
+ printf("%s\n", (char *)msg->data);
+
printf("Generated DLCX message:\n");
mgcp_msg.verb = MGCP_VERB_DLCX;
mgcp_msg.presence =
@@ -242,6 +298,9 @@ void test_mgcp_client_cancel()
.conn_mode = MGCP_CONN_RECV_SEND,
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID
| MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE),
+ .ptime = 20,
+ .codecs[0] = CODEC_AMR_8000_1,
+ .codecs_len = 1
};
printf("\n%s():\n", __func__);
@@ -376,6 +435,99 @@ void test_sdp_section_start()
OSMO_ASSERT(!failures);
}
+static void test_map_pt_to_codec(void)
+{
+ /* Full form */
+ OSMO_ASSERT(map_str_to_codec("PCMU/8000/1") == CODEC_PCMU_8000_1);
+ OSMO_ASSERT(map_str_to_codec("GSM/8000/1") == CODEC_GSM_8000_1);
+ OSMO_ASSERT(map_str_to_codec("PCMA/8000/1") == CODEC_PCMA_8000_1);
+ OSMO_ASSERT(map_str_to_codec("G729/8000/1") == CODEC_G729_8000_1);
+ OSMO_ASSERT(map_str_to_codec("GSM-EFR/8000/1") == CODEC_GSMEFR_8000_1);
+ OSMO_ASSERT(map_str_to_codec("GSM-HR-08/8000/1") == CODEC_GSMHR_8000_1);
+ OSMO_ASSERT(map_str_to_codec("AMR/8000/1") == CODEC_AMR_8000_1);
+ OSMO_ASSERT(map_str_to_codec("AMR-WB/16000/1") == CODEC_AMRWB_16000_1);
+
+ /* Short form */
+ OSMO_ASSERT(map_str_to_codec("GSM-EFR") == CODEC_GSMEFR_8000_1);
+ OSMO_ASSERT(map_str_to_codec("G729") == CODEC_G729_8000_1);
+ OSMO_ASSERT(map_str_to_codec("GSM-HR-08") == CODEC_GSMHR_8000_1);
+
+ /* We do not care about what is after the first delimiter */
+ OSMO_ASSERT(map_str_to_codec("AMR-WB/123///456") == CODEC_AMRWB_16000_1);
+ OSMO_ASSERT(map_str_to_codec("PCMA/asdf") == CODEC_PCMA_8000_1);
+ OSMO_ASSERT(map_str_to_codec("GSM/qwertz") == CODEC_GSM_8000_1);
+
+ /* A trailing delimiter should not hurt */
+ OSMO_ASSERT(map_str_to_codec("AMR/") == CODEC_AMR_8000_1);
+ OSMO_ASSERT(map_str_to_codec("G729/") == CODEC_G729_8000_1);
+ OSMO_ASSERT(map_str_to_codec("GSM/") == CODEC_GSM_8000_1);
+
+ /* This is expected to fail */
+ OSMO_ASSERT(map_str_to_codec("INVALID/1234/7") == -1);
+ OSMO_ASSERT(map_str_to_codec(NULL) == -1);
+ OSMO_ASSERT(map_str_to_codec("") == -1);
+ OSMO_ASSERT(map_str_to_codec("/////") == -1);
+
+ /* The buffers are 64 bytes long, check what happens with overlong
+ * strings as input (This schould still work.) */
+ OSMO_ASSERT(map_str_to_codec("AMR-WB/16000/1############################################################################################################") == CODEC_AMRWB_16000_1);
+
+ /* This should not work, as there is no delimiter after the codec
+ * name */
+ OSMO_ASSERT(map_str_to_codec("AMR-WB####################################################################################################################") == -1);
+}
+
+static void test_map_codec_to_pt_and_map_pt_to_codec(void)
+{
+ struct ptmap ptmap[10];
+ unsigned int ptmap_len;
+ unsigned int i;
+
+ ptmap[0].codec = CODEC_GSMEFR_8000_1;
+ ptmap[0].pt = 96;
+ ptmap[1].codec = CODEC_GSMHR_8000_1;
+ ptmap[1].pt = 97;
+ ptmap[2].codec = CODEC_AMR_8000_1;
+ ptmap[2].pt = 98;
+ ptmap[3].codec = CODEC_AMRWB_16000_1;
+ ptmap[3].pt = 99;
+ ptmap_len = 4;
+
+ /* Mappings that are covered by the table */
+ for (i = 0; i < ptmap_len; i++)
+ printf(" %u => %u\n", ptmap[i].codec, map_codec_to_pt(ptmap, ptmap_len, ptmap[i].codec));
+ for (i = 0; i < ptmap_len; i++)
+ printf(" %u <= %u\n", ptmap[i].pt, map_pt_to_codec(ptmap, ptmap_len, ptmap[i].pt));
+ printf("\n");
+
+ /* Map some codecs/payload types from the static range, result must
+ * always be a 1:1 mapping */
+ printf(" %u => %u\n", CODEC_PCMU_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_PCMU_8000_1));
+ printf(" %u => %u\n", CODEC_GSM_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_GSM_8000_1));
+ printf(" %u => %u\n", CODEC_PCMA_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_PCMA_8000_1));
+ printf(" %u => %u\n", CODEC_G729_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_G729_8000_1));
+ printf(" %u <= %u\n", CODEC_PCMU_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_PCMU_8000_1));
+ printf(" %u <= %u\n", CODEC_GSM_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_GSM_8000_1));
+ printf(" %u <= %u\n", CODEC_PCMA_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_PCMA_8000_1));
+ printf(" %u <= %u\n", CODEC_G729_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_G729_8000_1));
+ printf("\n");
+
+ /* Try to do mappings from statically defined range to danymic range and vice versa. This
+ * is illegal and should result into a 1:1 mapping */
+ ptmap[3].codec = CODEC_AMRWB_16000_1;
+ ptmap[3].pt = 2;
+ ptmap[4].codec = CODEC_PCMU_8000_1;
+ ptmap[4].pt = 100;
+ ptmap_len = 5;
+
+ /* Apply all mappings again, the illegal ones we defined should result into 1:1 mappings */
+ for (i = 0; i < ptmap_len; i++)
+ printf(" %u => %u\n", ptmap[i].codec, map_codec_to_pt(ptmap, ptmap_len, ptmap[i].codec));
+ for (i = 0; i < ptmap_len; i++)
+ printf(" %u <= %u\n", ptmap[i].pt, map_pt_to_codec(ptmap, ptmap_len, ptmap[i].pt));
+ printf("\n");
+}
+
static const struct log_info_cat log_categories[] = {
};
@@ -403,6 +555,8 @@ int main(int argc, char **argv)
test_mgcp_msg();
test_mgcp_client_cancel();
test_sdp_section_start();
+ test_map_codec_to_pt_and_map_pt_to_codec();
+ test_map_pt_to_codec();
printf("Done\n");
fprintf(stderr, "Done\n");
diff --git a/tests/mgcp_client/mgcp_client_test.err b/tests/mgcp_client/mgcp_client_test.err
index 7309242..1d5a1a0 100644
--- a/tests/mgcp_client/mgcp_client_test.err
+++ b/tests/mgcp_client/mgcp_client_test.err
@@ -62,4 +62,8 @@ 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 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
+DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
Done
diff --git a/tests/mgcp_client/mgcp_client_test.ok b/tests/mgcp_client/mgcp_client_test.ok
index fc6db30..454ee3d 100644
--- a/tests/mgcp_client/mgcp_client_test.ok
+++ b/tests/mgcp_client/mgcp_client_test.ok
@@ -18,8 +18,9 @@ o=- 1 23 IN IP4 10.9.1.120
s=-
c=IN IP4 10.9.1.120
t=0 0
-m=audio 16002 RTP/AVP 98
-a=rtpmap:98 AMR/8000
+m=audio 16002 RTP/AVP 110 96
+a=rtpmap:110 AMR/8000
+a=rtpmap:96 GSM-EFR/8000
a=ptime:20
-----
@@ -29,16 +30,39 @@ response cb received:
head.comment = OK
audio_port = 16002
audio_ip = 10.9.1.120
+ ptime = 20
+ codecs_len = 2
+ codecs[0] = 112
+ codecs[1] = 110
+ ptmap_len = 2
+ ptmap[0].codec = 112
+ ptmap[0].pt = 110
+ ptmap[1].codec = 110
+ ptmap[1].pt = 96
Generated CRCX message:
CRCX 1 23@mgw MGCP 1.0
C: 2f
I: 11
-L: p:20, a:AMR, nt:IN
+L: p:20, a:GSM, nt:IN
+M: sendrecv
+
+Generated CRCX message (two codecs):
+CRCX 2 23@mgw MGCP 1.0
+C: 2f
+I: 11
+L: p:20, a:GSM;AMR, nt:IN
+M: sendrecv
+
+Generated CRCX message (three codecs, one with custom pt):
+CRCX 3 23@mgw MGCP 1.0
+C: 2f
+I: 11
+L: p:20, a:GSM;AMR;GSM-EFR, nt:IN
M: sendrecv
Generated MDCX message:
-MDCX 2 23@mgw MGCP 1.0
+MDCX 4 23@mgw MGCP 1.0
C: 2f
I: 11
M: sendrecv
@@ -48,18 +72,50 @@ o=- 2f 23 IN IP4 127.0.0.1
s=-
c=IN IP4 192.168.100.23
t=0 0
-m=audio 1234 RTP/AVP 255
+m=audio 1234 RTP/AVP 3
+a=ptime:20
+
+Generated MDCX message (two codecs):
+MDCX 5 23@mgw MGCP 1.0
+C: 2f
+I: 11
+M: sendrecv
+
+v=0
+o=- 2f 23 IN IP4 127.0.0.1
+s=-
+c=IN IP4 192.168.100.23
+t=0 0
+m=audio 1234 RTP/AVP 3 112
+a=rtpmap:112 AMR/8000/1
+a=ptime:20
+
+Generated MDCX message (three codecs, one with custom pt):
+MDCX 6 23@mgw MGCP 1.0
+C: 2f
+I: 11
+M: sendrecv
+
+v=0
+o=- 2f 23 IN IP4 127.0.0.1
+s=-
+c=IN IP4 192.168.100.23
+t=0 0
+m=audio 1234 RTP/AVP 3 112 96
+a=rtpmap:112 AMR/8000/1
+a=rtpmap:96 GSM-EFR/8000/1
+a=ptime:20
Generated DLCX message:
-DLCX 3 23@mgw MGCP 1.0
+DLCX 7 23@mgw MGCP 1.0
C: 2f
I: 11
Generated AUEP message:
-AUEP 4 23@mgw MGCP 1.0
+AUEP 8 23@mgw MGCP 1.0
Generated RSIP message:
-RSIP 5 23@mgw MGCP 1.0
+RSIP 9 23@mgw MGCP 1.0
Overfolow test:
@@ -102,4 +158,33 @@ test_sdp_section_start() test [7]:
test_sdp_section_start() test [8]:
test_sdp_section_start() test [9]:
+ 110 => 96
+ 111 => 97
+ 112 => 98
+ 113 => 99
+ 96 <= 110
+ 97 <= 111
+ 98 <= 112
+ 99 <= 113
+
+ 0 => 0
+ 3 => 3
+ 8 => 8
+ 18 => 18
+ 0 <= 0
+ 3 <= 3
+ 8 <= 8
+ 18 <= 18
+
+ 110 => 96
+ 111 => 97
+ 112 => 98
+ 113 => 113
+ 0 => 0
+ 96 <= 110
+ 97 <= 111
+ 98 <= 112
+ 2 <= 2
+ 100 <= 100
+
Done