mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-11-01 12:33:49 +00:00
Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d236cca56 | ||
|
|
2fdedc0715 | ||
|
|
4a09ace3b4 | ||
|
|
12c45577bb | ||
|
|
b317b0056a | ||
|
|
a4ebbf7dbe | ||
|
|
8b23fb916a | ||
|
|
c3ff38ee4c | ||
|
|
eeee783db2 | ||
|
|
436f4dee17 | ||
|
|
ea4aa7d3c1 | ||
|
|
2140c20a0b | ||
|
|
485e085140 | ||
|
|
c8b8106cb5 | ||
|
|
c506ded3dd | ||
|
|
e96ad8f422 | ||
|
|
9253bb193f | ||
|
|
6018cc4d8a | ||
|
|
6d629768c7 | ||
|
|
5ec1c13221 | ||
|
|
f63ef75d9f | ||
|
|
741d0ece37 | ||
|
|
60eaa0d909 | ||
|
|
8d9d1d5e72 | ||
|
|
533cf424e7 | ||
|
|
6f4cd1b7e1 | ||
|
|
9d719b5d96 | ||
|
|
79fd5a4f47 | ||
|
|
5d0080b69d | ||
|
|
43279942c3 | ||
|
|
2eacae8f2e | ||
|
|
403d5f1489 | ||
|
|
f227633959 | ||
|
|
83c8846728 | ||
|
|
78ee99adcf | ||
|
|
de4090d920 | ||
|
|
732cadd2c8 | ||
|
|
6747b60a95 | ||
|
|
90ae9ed0a2 | ||
|
|
206be49c69 | ||
|
|
45559e9230 | ||
|
|
f94b846224 | ||
|
|
3ea3a69821 | ||
|
|
46ddc65626 | ||
|
|
71f313749e | ||
|
|
80a5abbbe3 | ||
|
|
ba812f7e86 | ||
|
|
345c37a543 | ||
|
|
b3457cd250 | ||
|
|
fcf1864db3 | ||
|
|
9221b67f3b | ||
|
|
732d595a26 | ||
|
|
3bc9d53628 | ||
|
|
b5a4f45dbf | ||
|
|
e6bafbf3e3 | ||
|
|
5c7b128166 | ||
|
|
5469edc278 | ||
|
|
66952183a9 | ||
|
|
cc97f58d96 | ||
|
|
5d6931881a | ||
|
|
0f4be7699b | ||
|
|
546983cdbc | ||
|
|
c04de4e7bb | ||
|
|
d46cdd80de | ||
|
|
6676e63c89 | ||
|
|
1f99c3aaae | ||
|
|
e6ef4e7484 | ||
|
|
b0fa041a28 | ||
|
|
e17bf77bdd | ||
|
|
802b1fad61 | ||
|
|
8a8973eec2 | ||
|
|
17e7ea66e4 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -8,6 +8,8 @@ Makefile
|
||||
Makefile.in
|
||||
bscconfig.h
|
||||
bscconfig.h.in
|
||||
include/osmocom/mgcp_client/mgcp_common.h
|
||||
include/osmocom/mgcp_client/version.h
|
||||
src/osmo-mgw/osmo-mgw
|
||||
*.*~
|
||||
*.sw?
|
||||
|
||||
@@ -9,3 +9,4 @@
|
||||
#library what description / commit summary line
|
||||
libosmocore bump_dep; workaround Bump libosmocore version dependency after I68328adb952ca8833ba047cb3b49ccc6f8a1f1b5
|
||||
has been merged to libosmocore.git; then remove my_msgb_copy_c wrapper function.
|
||||
libosmocodec bump_dep We depend on the additions of I2c510ac62a0786c137115c45eee7a48b9736265f
|
||||
|
||||
15
configure.ac
15
configure.ac
@@ -44,13 +44,14 @@ AC_SEARCH_LIBS([dlsym], [dl dld], [LIBRARY_DLSYM="$LIBS";LIBS=""])
|
||||
AC_SUBST(LIBRARY_DLSYM)
|
||||
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCODEC, libosmocodec >= 1.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 2.0.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 2.0.0)
|
||||
|
||||
CFLAGS="$CFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread"
|
||||
CPPFLAGS="$CPPFLAGS -DBUILDING_LIBOSMOMGCPCLIENT -pthread"
|
||||
|
||||
@@ -22,7 +22,7 @@ export deps inst
|
||||
osmo-clean-workspace.sh
|
||||
|
||||
mkdir "$deps" || true
|
||||
osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false
|
||||
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
||||
|
||||
verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
|
||||
|
||||
@@ -30,8 +30,8 @@ export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
export LD_LIBRARY_PATH="$inst/lib"
|
||||
export PATH="$inst/bin:$PATH"
|
||||
|
||||
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
|
||||
osmo-build-dep.sh libosmo-abis
|
||||
osmo-build-dep.sh libosmo-netif
|
||||
|
||||
# Additional configure options and depends
|
||||
CONFIG=""
|
||||
|
||||
49
debian/changelog
vendored
49
debian/changelog
vendored
@@ -1,3 +1,52 @@
|
||||
osmo-mgw (1.14.0) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* jenkins.sh: libosmo-netif no longer depends on libosmo-abis
|
||||
* jenkins.sh: Use --disable-doxygen configure param
|
||||
* mgcp-client: Fix wrong value passed to strerror()
|
||||
* mgcp_client_internal.h: Add missing header dependency
|
||||
* mgcp-cli: Mark iofd ptr as NULL when freed
|
||||
* mgcp-cli: Improve error handling around mgcp_msg_gen() return
|
||||
* mgcp-cli: Fix filling in wrong local IP address of SDP Origin o=
|
||||
* mgw: Drop own MGCP extension 'noanswer'
|
||||
* cosmetic: mgw: Fix indentation whitespace
|
||||
* mgcp-client: Fix regression checking null ptr
|
||||
* mgw: Rename and move code freeing endp connection
|
||||
* mgw: Rename and move several get_conn funcs acting on endp object
|
||||
* mgw: mgcp_network.c: Simplify use of conn_rtp ptr
|
||||
* mgw: Clean up access to conn_rtp from conn
|
||||
* mgw: Avoid 2nd lookup of conn in endp during CRCX
|
||||
* cosmetic: mgw: iuup: Update comment
|
||||
* mgw: constify mgcp_endp_avail() param
|
||||
* mgcp_endp: Add helpers accessing endp connections
|
||||
* mgw: mgcp_protocol: assert freeing last conn allows creating new conn
|
||||
* mgw: Split conn mode parsing and applying into conn
|
||||
* mgw: CRCXMDCX/DLCX: rename conn and conn_rtp variables
|
||||
* mgw: Use bool instead of int in local var
|
||||
* Rename mgcp_free_rtp_port() to mgcp_rtp_end_free_port()
|
||||
* mgw: Split mgcp_rtp_end to its own file
|
||||
* mgw: Introduce mgcp_rtp_end_init()
|
||||
* mgw: Introduce struct mgcp_codecset struct
|
||||
* mgw: Cleanup rtp_endp fields in its own function
|
||||
* mgw: Move force_ptime logic outside of main CRCX func handler
|
||||
* mgw: Move several params setting to mgcp_rtp_end_init()
|
||||
* mgw: Simplify and redo code around ssrc patch feature
|
||||
* mgw: Clean up code allocating conn_rtp rtp/rtcp sockets
|
||||
* mgw: Rename and cleanup code allocating rtp/rtcp ports in trunk
|
||||
* mgw: CRCX: Split mgcp header pars parsing into a previous step
|
||||
* mgw: MDCX: Split mgcp header pars parsing into a previous step
|
||||
* mgw: DLCX: Split mgcp header pars parsing into a previous step
|
||||
* mgw: Decouple SDP parsing step from conn obj update
|
||||
* mgw: Remove wrong TODO comment
|
||||
* mgw: MDCX: Simplify early return code paths
|
||||
|
||||
[ Mychaela N. Falconia ]
|
||||
* E1 cosmetic: reduce white space in hard-coded TRAU-DL frames
|
||||
* E1: replace idle_tf_efr[] with a better version
|
||||
* E1: replace idle_tf_fr[] with a better version
|
||||
|
||||
-- Oliver Smith <osmith@sysmocom.de> Wed, 12 Feb 2025 12:30:33 +0100
|
||||
|
||||
osmo-mgw (1.13.1) unstable; urgency=medium
|
||||
|
||||
[ Philipp Maier ]
|
||||
|
||||
6
debian/control
vendored
6
debian/control
vendored
@@ -6,9 +6,9 @@ Build-Depends: debhelper (>= 10),
|
||||
dh-autoreconf,
|
||||
pkg-config,
|
||||
autotools-dev,
|
||||
libosmocore-dev (>= 1.10.0),
|
||||
libosmo-netif-dev (>= 1.5.0),
|
||||
libosmo-abis-dev (>= 1.6.0),
|
||||
libosmocore-dev (>= 1.11.0),
|
||||
libosmo-netif-dev (>= 1.6.0),
|
||||
libosmo-abis-dev (>= 2.0.0),
|
||||
osmo-gsm-manuals-dev (>= 1.6.0)
|
||||
Standards-Version: 3.9.8
|
||||
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
|
||||
|
||||
@@ -70,3 +70,85 @@ a=ptime:40
|
||||
=== `X-Osmux`
|
||||
|
||||
See <<mgcp-extension-osmux>>
|
||||
|
||||
=== Non-standard SDP codec parameters
|
||||
|
||||
CRCX command message normally includes an SDP block that specifies the RTP
|
||||
address of the connecting peer and the list of codecs accepted by that entity.
|
||||
Codec specifications may include optional parameters given via `a=fmtp` lines;
|
||||
one well-known parameter is `octet-align`, defined for AMR and AMR-WB codecs.
|
||||
|
||||
In addition to standard (RFC-defined) codec parameters (of which the
|
||||
just-mentioned `octet-align` parameter is the only one implemented so far),
|
||||
OsmoMGW also understands some non-standard (Osmocom-private) codec parameters
|
||||
as listed below.
|
||||
These Osmocom-private codec parameters are meaningful only in the context
|
||||
of OsmoBSC driving an E1 Abis MGW, which may be either OsmoMGW or a specialized
|
||||
third-party replacement.
|
||||
|
||||
The MGCP extension described in this section is meaningful only for E1
|
||||
endpoints - it has absolutely no effect on `rtpbridge` endpoints!
|
||||
|
||||
==== `tw-ts-001` parameter for FR and EFR codecs
|
||||
|
||||
GSM Full Rate codec is known in SDP as `GSM`; GSM Enhanced Full Rate codec
|
||||
is known in SDP as `GSM-EFR`.
|
||||
When either codec is to be transported in RTP, there is a choice between
|
||||
two different payload formats: RFC 3551 or TW-TS-001.
|
||||
RFC 3551 is the standard in non-GSM Internet applications, and it is the
|
||||
format specified in later 3GPP releases for IP-based transport of
|
||||
compressed speech - however, it exhibits functional regressions compared
|
||||
to the original TRAU-UL frame format of GSM 08.60.
|
||||
TW-TS-001 is an enhanced alternative payload format for the same codecs
|
||||
that restores the original functionality and semantics of TRAU-UL frames.
|
||||
|
||||
In the context of CRCX or MDCX command messages from OsmoBSC to an E1 Abis MGW,
|
||||
`tw-ts-001` codec parameter (attached to either `GSM` or `GSM-EFR` codec)
|
||||
instructs the MGW whether or not it should emit the extended payload format
|
||||
of TW-TS-001 for GSM uplink traffic converted to RTP.
|
||||
In the absence of this parameter, IETF-standard and 3GPP-standard RFC 3551
|
||||
format is emitted; if TW-TS-001 output is desired, please specify
|
||||
`tw-ts-001=1` on the appropriate `a=fmtp` line.
|
||||
|
||||
.Example: `CRCX` message requesting EFR codec with TW-TS-001 extensions
|
||||
----
|
||||
CRCX 2 ds/e1-0/s-2/su16-2@mgw MGCP 1.0
|
||||
M: recvonly
|
||||
C: 2
|
||||
L: p:20
|
||||
|
||||
v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 1234 RTP/AVP 110
|
||||
a=rtpmap:110 GSM-EFR/8000/1
|
||||
a=fmtp:110 tw-ts-001=1
|
||||
----
|
||||
|
||||
==== `tw-ts-002` parameter for HR codec
|
||||
|
||||
GSM Half Rate codec is known in SDP as `GSM-HR-08`.
|
||||
The same dichotomy that exists between RFC 3551 and TW-TS-001 RTP payload
|
||||
formats for FR and EFR codecs also exists for HR codec, except that
|
||||
the two opposing alternatives are now RFC 5993 and TW-TS-002.
|
||||
|
||||
In the context of CRCX or MDCX command messages from OsmoBSC to an E1 Abis MGW,
|
||||
`tw-ts-002` codec parameter (attached to `GSM-HR-08` codec)
|
||||
instructs the MGW whether or not it should emit the extended payload format
|
||||
of TW-TS-002 for GSM uplink traffic converted to RTP.
|
||||
In the absence of this parameter, IETF-standard and 3GPP-standard RFC 5993
|
||||
format is emitted; if TW-TS-002 output is desired, please specify
|
||||
`tw-ts-002=1` on the appropriate `a=fmtp` line.
|
||||
|
||||
.Example: `CRCX` message requesting HR codec with TW-TS-002 extensions
|
||||
----
|
||||
CRCX 2 ds/e1-0/s-2/su8-2@mgw MGCP 1.0
|
||||
M: recvonly
|
||||
C: 2
|
||||
L: p:20
|
||||
|
||||
v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 1234 RTP/AVP 111
|
||||
a=rtpmap:111 GSM-HR-08/8000/1
|
||||
a=fmtp:111 tw-ts-002=1
|
||||
----
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
noinst_HEADERS = \
|
||||
vty.h \
|
||||
mgcp_msg.h \
|
||||
mgcp_codec.h \
|
||||
mgcp_conn.h \
|
||||
mgcp_stat.h \
|
||||
mgcp_endp.h \
|
||||
@@ -13,4 +14,5 @@ noinst_HEADERS = \
|
||||
mgcp_network.h \
|
||||
mgcp_protocol.h \
|
||||
mgcp_iuup.h \
|
||||
mgcp_rtp_end.h \
|
||||
$(NULL)
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
|
||||
#include "mgcp_ratectr.h"
|
||||
|
||||
extern void *tall_mgw_ctx;
|
||||
|
||||
#define RTP_PORT_DEFAULT_RANGE_START 16002
|
||||
#define RTP_PORT_DEFAULT_RANGE_END RTP_PORT_DEFAULT_RANGE_START + 64
|
||||
|
||||
@@ -50,11 +52,6 @@ struct mgcp_endpoint;
|
||||
struct mgcp_config;
|
||||
struct mgcp_trunk;
|
||||
struct mgcp_rtp_end;
|
||||
|
||||
#define MGCP_ENDP_CRCX 1
|
||||
#define MGCP_ENDP_DLCX 2
|
||||
#define MGCP_ENDP_MDCX 3
|
||||
|
||||
/*
|
||||
* what to do with the msg?
|
||||
* - continue as usual?
|
||||
@@ -133,10 +130,6 @@ struct mgcp_config {
|
||||
char source_addr[INET6_ADDRSTRLEN];
|
||||
char call_agent_addr[INET6_ADDRSTRLEN];
|
||||
|
||||
/* RTP processing */
|
||||
mgcp_processing rtp_processing_cb;
|
||||
mgcp_processing_setup setup_rtp_processing_cb;
|
||||
|
||||
struct osmo_wqueue gw_fd;
|
||||
|
||||
struct mgcp_port_range net_ports;
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
|
||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20
|
||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
|
||||
#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
|
||||
@@ -8,14 +12,37 @@
|
||||
|
||||
#define PTYPE_UNDEFINED (-1)
|
||||
|
||||
struct mgcp_conn_rtp;
|
||||
struct mgcp_rtp_codec {
|
||||
uint32_t rate;
|
||||
int channels;
|
||||
uint32_t frame_duration_num;
|
||||
uint32_t frame_duration_den;
|
||||
|
||||
int payload_type;
|
||||
char audio_name[64];
|
||||
char subtype_name[64];
|
||||
|
||||
bool param_present;
|
||||
struct mgcp_codec_param param;
|
||||
};
|
||||
|
||||
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, const struct mgcp_codec_param *param);
|
||||
int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst);
|
||||
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
|
||||
const char *subtype_name, unsigned int match_nr);
|
||||
bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec);
|
||||
bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec);
|
||||
struct mgcp_rtp_codec *mgcp_codec_from_pt(struct mgcp_conn_rtp *conn, int payload_type);
|
||||
|
||||
struct mgcp_rtp_codecset {
|
||||
/* 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;
|
||||
};
|
||||
|
||||
void mgcp_codecset_reset(struct mgcp_rtp_codecset *cset);
|
||||
void mgcp_codecset_summary(struct mgcp_rtp_codecset *cset, const char *prefix_str);
|
||||
int mgcp_codecset_add_codec(struct mgcp_rtp_codecset *cset, int payload_type,
|
||||
const char *audio_name, const struct mgcp_codec_param *param);
|
||||
int mgcp_codecset_decide(struct mgcp_rtp_codecset *cset_src, struct mgcp_rtp_codecset *cset_dst);
|
||||
const struct mgcp_rtp_codec *mgcp_codecset_pt_find_by_subtype_name(const struct mgcp_rtp_codecset *cset,
|
||||
const char *subtype_name, unsigned int match_nr);
|
||||
struct mgcp_rtp_codec *mgcp_codecset_find_codec_from_pt(struct mgcp_rtp_codecset *cset, int payload_type);
|
||||
|
||||
@@ -63,6 +63,10 @@ enum mgcp_x_osmo_ign {
|
||||
struct mgcp_codec_param {
|
||||
bool amr_octet_aligned_present;
|
||||
bool amr_octet_aligned;
|
||||
bool fr_efr_twts001_present;
|
||||
bool fr_efr_twts001;
|
||||
bool hr_twts002_present;
|
||||
bool hr_twts002;
|
||||
};
|
||||
|
||||
/* Ensure that the msg->l2h is NUL terminated. */
|
||||
|
||||
@@ -24,11 +24,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
#include <osmocom/mgcp/mgcp_network.h>
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/iuup.h>
|
||||
#include <osmocom/mgcp/mgcp_rtp_end.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#define LOGPCONN(conn, cat, level, fmt, args...) \
|
||||
@@ -237,15 +240,16 @@ static inline bool mgcp_conn_rtp_is_iuup(const struct mgcp_conn_rtp *conn)
|
||||
return conn->type == MGCP_RTP_IUUP;
|
||||
}
|
||||
|
||||
static inline struct mgcp_conn_rtp *mgcp_conn_get_conn_rtp(struct mgcp_conn *conn)
|
||||
{
|
||||
OSMO_ASSERT(conn->type == MGCP_CONN_TYPE_RTP);
|
||||
return &conn->u.rtp;
|
||||
}
|
||||
|
||||
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);
|
||||
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
|
||||
const char *id);
|
||||
void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id);
|
||||
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp);
|
||||
void mgcp_conn_free_all(struct mgcp_endpoint *endp);
|
||||
void mgcp_conn_free(struct mgcp_conn *conn);
|
||||
int mgcp_conn_set_mode(struct mgcp_conn *conn, enum mgcp_connection_mode mode);
|
||||
char *mgcp_conn_dump(struct mgcp_conn *conn);
|
||||
struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn);
|
||||
struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp);
|
||||
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn);
|
||||
|
||||
@@ -22,6 +22,6 @@ static const uint8_t e1_rates[] = { 64, 32, 32, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8
|
||||
static const uint8_t e1_offsets[] = { 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
|
||||
int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs);
|
||||
void mgcp_e1_endp_update(struct mgcp_endpoint *endp);
|
||||
int mgcp_e1_endp_update(struct mgcp_endpoint *endp);
|
||||
void mgcp_e1_endp_release(struct mgcp_endpoint *endp, uint8_t ts);
|
||||
int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg);
|
||||
|
||||
@@ -124,6 +124,7 @@ struct mgcp_endpoint {
|
||||
struct osmo_fsm_inst *trau_sync_fi;
|
||||
struct osmo_trau2rtp_state *trau_rtp_st;
|
||||
uint8_t last_amr_ft;
|
||||
uint8_t rtp_extensions;
|
||||
struct mgcp_rtp_codec *last_codec;
|
||||
} e1;
|
||||
|
||||
@@ -131,18 +132,28 @@ struct mgcp_endpoint {
|
||||
|
||||
struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, unsigned int index);
|
||||
int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid);
|
||||
void mgcp_endp_update(struct mgcp_endpoint *endp);
|
||||
int mgcp_endp_update(struct mgcp_endpoint *endp, struct mgcp_conn *conn, enum mgcp_verb verb);
|
||||
bool mgcp_endp_is_wildcarded(const char *epname);
|
||||
bool mgcp_endp_is_null(const char *epname);
|
||||
struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname,
|
||||
const struct mgcp_trunk *trunk);
|
||||
struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname,
|
||||
struct mgcp_config *cfg);
|
||||
bool mgcp_endp_avail(struct mgcp_endpoint *endp);
|
||||
bool mgcp_endp_avail(const struct mgcp_endpoint *endp);
|
||||
unsigned int mgcp_endp_num_conns(const struct mgcp_endpoint *endp);
|
||||
bool mgcp_endp_is_full(const struct mgcp_endpoint *endp);
|
||||
void mgcp_endp_add_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
|
||||
void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
|
||||
void mgcp_endp_free_conn_oldest(struct mgcp_endpoint *endp);
|
||||
void mgcp_endp_free_conn_all(struct mgcp_endpoint *endp);
|
||||
void mgcp_endp_strip_name(char *epname_stripped, const char *epname,
|
||||
const struct mgcp_trunk *trunk);
|
||||
struct mgcp_endpoint *mgcp_endp_find_specific(const char *epname,
|
||||
const struct mgcp_trunk *trunk);
|
||||
void mgcp_endp_update_lco(struct mgcp_endpoint *endp, const struct mgcp_lco *lco);
|
||||
void mgcp_endp_release(struct mgcp_endpoint *endp);
|
||||
|
||||
struct mgcp_conn *mgcp_endp_get_conn(struct mgcp_endpoint *endp, const char *id);
|
||||
struct mgcp_conn *mgcp_endp_get_conn_oldest(struct mgcp_endpoint *endp);
|
||||
struct mgcp_conn_rtp *mgcp_endp_get_conn_rtp(struct mgcp_endpoint *endp,
|
||||
const char *id);
|
||||
|
||||
@@ -31,3 +31,4 @@ void mgcp_conn_iuup_cleanup(struct mgcp_conn_rtp *conn_rtp);
|
||||
int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg);
|
||||
int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn_rtp *conn_dest_rtp, struct msgb *msg);
|
||||
int mgcp_conn_iuup_send_dummy(struct mgcp_conn_rtp *conn_rtp);
|
||||
int mgcp_conn_iuup_event_rx_crcx_mdcx(struct mgcp_conn_rtp *conn_rtp);
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
|
||||
struct mgcp_conn;
|
||||
struct mgcp_parse_data;
|
||||
struct mgcp_endpoint;
|
||||
@@ -34,14 +36,14 @@ struct mgcp_trunk;
|
||||
|
||||
void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble);
|
||||
|
||||
int mgcp_parse_conn_mode(const char *msg, struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn *conn);
|
||||
enum mgcp_connection_mode mgcp_parse_conn_mode(const char *msg);
|
||||
|
||||
int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data);
|
||||
int mgcp_parse_hdr_pars(struct mgcp_parse_data *pdata);
|
||||
|
||||
int mgcp_parse_osmux_cid(const char *line);
|
||||
|
||||
bool mgcp_check_param(const struct mgcp_endpoint *endp, struct mgcp_trunk *trunk, const char *line);
|
||||
bool mgcp_check_param(const char *line);
|
||||
|
||||
int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid);
|
||||
|
||||
|
||||
@@ -74,65 +74,6 @@ struct mgcp_rtp_state {
|
||||
uint32_t alt_rtp_tx_ssrc;
|
||||
};
|
||||
|
||||
struct mgcp_rtp_codec {
|
||||
uint32_t rate;
|
||||
int channels;
|
||||
uint32_t frame_duration_num;
|
||||
uint32_t frame_duration_den;
|
||||
|
||||
int payload_type;
|
||||
char audio_name[64];
|
||||
char subtype_name[64];
|
||||
|
||||
bool param_present;
|
||||
struct mgcp_codec_param param;
|
||||
};
|
||||
|
||||
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
|
||||
struct mgcp_rtp_end {
|
||||
/* remote IP address of the RTP socket */
|
||||
struct osmo_sockaddr addr;
|
||||
|
||||
/* in network byte order */
|
||||
uint16_t rtcp_port;
|
||||
|
||||
/* 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 */
|
||||
/* are we transmitting packets (true) or dropping (false) outbound packets */
|
||||
bool output_enabled;
|
||||
/* FIXME: This parameter can be set + printed, but is nowhere used! */
|
||||
int force_output_ptime;
|
||||
|
||||
/* RTP patching */
|
||||
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;
|
||||
bool rfc5993_hr_convert;
|
||||
|
||||
/* Each end has a separate socket for RTP and RTCP */
|
||||
struct osmo_io_fd *rtp;
|
||||
struct osmo_io_fd *rtcp;
|
||||
|
||||
/* local UDP port number of the RTP socket; RTCP is +1 */
|
||||
int local_port;
|
||||
/* where the endpoint RTP connection binds to, set during CRCX and
|
||||
* possibly updated during MDCX */
|
||||
char local_addr[INET6_ADDRSTRLEN];
|
||||
};
|
||||
|
||||
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end);
|
||||
|
||||
struct mgcp_rtp_tap {
|
||||
/* is this tap active (1) or not (0) */
|
||||
int enabled;
|
||||
@@ -150,27 +91,13 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg);
|
||||
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
|
||||
int mgcp_dispatch_e1_bridge_cb(struct msgb *msg);
|
||||
void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
|
||||
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
void mgcp_free_rtp_port(struct mgcp_rtp_end *end);
|
||||
int mgcp_conn_rtp_bind_rtp_ports(struct mgcp_conn_rtp *conn, int rtp_port);
|
||||
void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_state *state,
|
||||
struct mgcp_rtp_end *rtp_end,
|
||||
struct osmo_sockaddr *addr, struct msgb *msg);
|
||||
int mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
|
||||
|
||||
/* payload processing default functions */
|
||||
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, struct msgb *msg);
|
||||
|
||||
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn_dst,
|
||||
struct mgcp_conn_rtp *conn_src);
|
||||
|
||||
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
||||
const struct mgcp_rtp_codec **codec,
|
||||
const char **fmtp_extra,
|
||||
struct mgcp_conn_rtp *conn);
|
||||
|
||||
/* internal RTP Annex A counting */
|
||||
void mgcp_rtp_annex_count(const struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
|
||||
const uint16_t seq, const int32_t transit,
|
||||
|
||||
@@ -1,30 +1,140 @@
|
||||
#pragma once
|
||||
|
||||
/* Internal structure while parsing a request */
|
||||
struct mgcp_parse_data {
|
||||
struct mgcp_config *cfg;
|
||||
char *epname;
|
||||
char *trans;
|
||||
char *save;
|
||||
#include <stdint.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
|
||||
enum mgcp_verb {
|
||||
MGCP_VERB_CRCX,
|
||||
MGCP_VERB_MDCX,
|
||||
MGCP_VERB_DLCX,
|
||||
MGCP_VERB_AUEP,
|
||||
MGCP_VERB_RQNT,
|
||||
MGCP_VERB_RSIP,
|
||||
};
|
||||
extern const struct value_string mgcp_verb_names[];
|
||||
static inline const char *mgcp_verb_name(enum mgcp_verb val)
|
||||
{ return get_value_string(mgcp_verb_names, val); }
|
||||
|
||||
|
||||
#define MGCP_PARSE_SDP_PTIME_UNSET (-1)
|
||||
#define MGCP_PARSE_SDP_MAXPTIME_UNSET (-1)
|
||||
#define MGCP_PARSE_SDP_RTP_PORT_UNSET (0)
|
||||
|
||||
struct mgcp_parse_sdp {
|
||||
int ptime;
|
||||
int maxptime;
|
||||
int rtp_port;
|
||||
struct osmo_sockaddr rem_addr; /* Only IP address, port is in rtp_port above */
|
||||
struct mgcp_rtp_codecset cset;
|
||||
};
|
||||
|
||||
static inline void mgcp_parse_sdp_init(struct mgcp_parse_sdp *sdp)
|
||||
{
|
||||
sdp->ptime = MGCP_PARSE_SDP_PTIME_UNSET;
|
||||
sdp->maxptime = MGCP_PARSE_SDP_MAXPTIME_UNSET;
|
||||
sdp->rtp_port = MGCP_PARSE_SDP_RTP_PORT_UNSET;
|
||||
sdp->rem_addr = (struct osmo_sockaddr){ .u.sa.sa_family = AF_UNSPEC };
|
||||
mgcp_codecset_reset(&sdp->cset);
|
||||
}
|
||||
|
||||
/* Local connection options */
|
||||
struct mgcp_lco {
|
||||
char *string;
|
||||
char *codec;
|
||||
bool present;
|
||||
char *codec; /* talloc-allocated to some parent */
|
||||
int pkt_period_min; /* time in ms */
|
||||
int pkt_period_max; /* time in ms */
|
||||
};
|
||||
static inline void mgcp_lco_init(struct mgcp_lco *lco)
|
||||
{
|
||||
*lco = (struct mgcp_lco){};
|
||||
}
|
||||
char *get_lco_identifier(const char *options);
|
||||
int check_local_cx_options(void *ctx, const char *options);
|
||||
|
||||
#define MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET (-2)
|
||||
#define MGCP_PARSE_HDR_PARS_OSMUX_CID_WILDCARD (-1)
|
||||
|
||||
struct mgcp_parse_hdr_pars {
|
||||
const char *lco_string;
|
||||
struct mgcp_lco lco;
|
||||
const char *callid;
|
||||
const char *connid;
|
||||
enum mgcp_connection_mode mode;
|
||||
int remote_osmux_cid;
|
||||
bool have_sdp;
|
||||
/*! MGCP_X_OSMO_IGN_* flags from 'X-Osmo-IGN:' header */
|
||||
uint32_t x_osmo_ign;
|
||||
};
|
||||
|
||||
static inline void mgcp_parse_hdr_pars_init(struct mgcp_parse_hdr_pars *hpars)
|
||||
{
|
||||
hpars->lco_string = NULL;
|
||||
mgcp_lco_init(&hpars->lco);
|
||||
hpars->callid = NULL;
|
||||
hpars->connid = NULL;
|
||||
hpars->mode = MGCP_CONN_NONE;
|
||||
hpars->remote_osmux_cid = MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET;
|
||||
hpars->have_sdp = false;
|
||||
hpars->x_osmo_ign = 0;
|
||||
}
|
||||
|
||||
/* Internal structure while parsing a request */
|
||||
struct mgcp_request_data;
|
||||
struct mgcp_parse_data {
|
||||
struct mgcp_request_data *rq; /* backpointer to request context */
|
||||
char *save;
|
||||
/* MGCP Header: */
|
||||
char *epname;
|
||||
char *trans;
|
||||
struct mgcp_parse_hdr_pars hpars;
|
||||
/* MGCP Body: */
|
||||
struct mgcp_parse_sdp sdp;
|
||||
};
|
||||
|
||||
/* Request data passed to the request handler */
|
||||
struct mgcp_request_data {
|
||||
enum mgcp_verb verb;
|
||||
/* Verb string (e.g. "MDCX") */
|
||||
char name[4+1];
|
||||
|
||||
/* Global MGW config */
|
||||
struct mgcp_config *cfg;
|
||||
|
||||
/* parsing results from the MGCP header (trans id, endpoint name ...) */
|
||||
struct mgcp_parse_data *pdata;
|
||||
|
||||
/* pointer to endpoint resource (may be NULL for wildcarded requests) */
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
/* pointer to trunk resource */
|
||||
struct mgcp_trunk *trunk;
|
||||
|
||||
/* set to true when the request has been classified as wildcarded */
|
||||
bool wildcarded;
|
||||
|
||||
/* Set to true when the request is targeted at the "null" endpoint */
|
||||
bool null_endp;
|
||||
|
||||
/* contains cause code in case of problems during endp/trunk resolution */
|
||||
int mgcp_cause;
|
||||
};
|
||||
|
||||
char *mgcp_debug_get_last_endpoint_name(void);
|
||||
|
||||
char *get_lco_identifier(const char *options);
|
||||
int check_local_cx_options(void *ctx, const char *options);
|
||||
|
||||
struct mgcp_rtp_end;
|
||||
struct mgcp_endpoint;
|
||||
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
|
||||
struct mgcp_rtp_end *rtp);
|
||||
|
||||
uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
|
||||
const struct mgcp_rtp_end *rtp);
|
||||
|
||||
extern const struct value_string mgcp_connection_mode_strs[];
|
||||
static inline const char *mgcp_cmode_name(enum mgcp_connection_mode mode)
|
||||
{
|
||||
return get_value_string(mgcp_connection_mode_strs, mode);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ enum {
|
||||
MGCP_MDCX_FAIL_INVALID_MODE,
|
||||
MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS,
|
||||
MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC,
|
||||
MGCP_MDCX_FAIL_START_RTP,
|
||||
MGCP_MDCX_FAIL_BIND_PORT,
|
||||
MGCP_MDCX_FAIL_AVAIL,
|
||||
};
|
||||
|
||||
|
||||
53
include/osmocom/mgcp/mgcp_rtp_end.h
Normal file
53
include/osmocom/mgcp/mgcp_rtp_end.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/osmo_io.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
|
||||
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
|
||||
struct mgcp_rtp_end {
|
||||
struct mgcp_conn_rtp *conn_rtp; /* backpointer */
|
||||
/* remote IP address of the RTP socket */
|
||||
struct osmo_sockaddr addr;
|
||||
|
||||
/* in network byte order */
|
||||
uint16_t rtcp_port;
|
||||
|
||||
struct mgcp_rtp_codecset cset;
|
||||
|
||||
/* per endpoint data */
|
||||
int frames_per_packet;
|
||||
uint32_t packet_duration_ms;
|
||||
int maximum_packet_time; /* -1: not set */
|
||||
/* are we transmitting packets (true) or dropping (false) outbound packets */
|
||||
bool output_enabled;
|
||||
/* FIXME: This parameter can be set + printed, but is nowhere used! */
|
||||
int force_output_ptime;
|
||||
|
||||
/* RTP patching */
|
||||
bool force_constant_ssrc;
|
||||
/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
|
||||
int force_aligned_timing;
|
||||
bool rfc5993_hr_convert;
|
||||
|
||||
/* Each end has a separate socket for RTP and RTCP */
|
||||
struct osmo_io_fd *rtp;
|
||||
struct osmo_io_fd *rtcp;
|
||||
|
||||
/* local UDP port number of the RTP socket; RTCP is +1 */
|
||||
int local_port;
|
||||
/* where the endpoint RTP connection binds to, set during CRCX and
|
||||
* possibly updated during MDCX */
|
||||
char local_addr[INET6_ADDRSTRLEN];
|
||||
};
|
||||
|
||||
void mgcp_rtp_end_init(struct mgcp_rtp_end *end, struct mgcp_conn_rtp *conn_rtp);
|
||||
void mgcp_rtp_end_cleanup(struct mgcp_rtp_end *end);
|
||||
void mgcp_rtp_end_set_packet_duration_ms(struct mgcp_rtp_end *end, uint32_t packet_duration_ms);
|
||||
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end);
|
||||
void mgcp_rtp_end_free_port(struct mgcp_rtp_end *end);
|
||||
@@ -22,9 +22,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn,
|
||||
struct mgcp_parse_data *p);
|
||||
struct mgcp_parse_data;
|
||||
|
||||
int mgcp_parse_sdp_data(struct mgcp_parse_data *p);
|
||||
|
||||
int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
const struct mgcp_conn_rtp *conn, struct msgb *sdp,
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
#include <osmocom/gsm/i460_mux.h>
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_network.h>
|
||||
#include <osmocom/mgcp/mgcp_ratectr.h>
|
||||
|
||||
|
||||
#define LOGPTRUNK(trunk, cat, level, fmt, args...) \
|
||||
LOGP(cat, level, "trunk:%u " fmt, \
|
||||
trunk ? trunk->trunk_nr : 0, \
|
||||
@@ -34,7 +35,7 @@ struct mgcp_trunk {
|
||||
int keepalive_interval;
|
||||
|
||||
/* RTP patching */
|
||||
int force_constant_ssrc; /* 0: don't, 1: once */
|
||||
bool force_constant_ssrc;
|
||||
int force_aligned_timing;
|
||||
bool rfc5993_hr_convert;
|
||||
|
||||
@@ -78,6 +79,7 @@ struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_tr
|
||||
struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char *epname);
|
||||
int e1_trunk_nr_from_epname(unsigned int *trunk_nr, const char *epname);
|
||||
struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigned int num);
|
||||
int mgcp_trunk_allocate_conn_rtp_ports(struct mgcp_trunk *trunk, struct mgcp_conn_rtp *conn_rtp);
|
||||
|
||||
/* The virtual trunk is always created on trunk id 0 for historical reasons,
|
||||
* use this define constant as ID when allocating a virtual trunk. Other
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
BUILT_SOURCES = \
|
||||
mgcp_common.h \
|
||||
version.h \
|
||||
$(NULL)
|
||||
|
||||
noinst_HEADERS = \
|
||||
@@ -11,4 +12,20 @@ mgcp_common.h: $(top_srcdir)/include/osmocom/mgcp/mgcp_common.h
|
||||
echo -e "/*\n\n DO NOT EDIT THIS FILE!\n THIS IS OVERWRITTEN DURING BUILD\n This is an automatic copy of <osmocom/mgcp/mgcp_common.h>\n\n */" > mgcp_common.h
|
||||
cat $(top_srcdir)/include/osmocom/mgcp/mgcp_common.h >> mgcp_common.h
|
||||
|
||||
CLEANFILES = mgcp_common.h
|
||||
version.h: version.h.tpl
|
||||
$(AM_V_GEN)$(MKDIR_P) $(dir $@)
|
||||
$(AM_V_GEN)sed \
|
||||
-e "s/{{VERSION}}/$$(echo '@VERSION@' | cut -d. -f1-3)/g" \
|
||||
-e "s/{{VERSION_MAJOR}}/$$(echo '@VERSION@' | cut -d. -f1)/g" \
|
||||
-e "s/{{VERSION_MINOR}}/$$(echo '@VERSION@' | cut -d. -f2)/g" \
|
||||
-e "s/{{VERSION_PATCH}}/$$(echo '@VERSION@' | cut -d. -f3)/g" \
|
||||
$< > $@
|
||||
|
||||
EXTRA_DIST = \
|
||||
version.h.tpl \
|
||||
$(NULL)
|
||||
|
||||
CLEANFILES = \
|
||||
mgcp_common.h \
|
||||
version.h \
|
||||
$(NULL)
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <osmocom/core/osmo_io.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
|
||||
#define MSGB_CB_MGCP_TRANS_ID 0
|
||||
|
||||
/* Struct that holds one endpoint name */
|
||||
|
||||
16
include/osmocom/mgcp_client/version.h.tpl
Normal file
16
include/osmocom/mgcp_client/version.h.tpl
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#define LIBOSMO_MGCP_CLIENT_VERSION {{VERSION}}
|
||||
#define LIBOSMO_MGCP_CLIENT_VERSION_STR "{{VERSION}}"
|
||||
|
||||
#define LIBOSMO_MGCP_CLIENT_VERSION_MAJOR {{VERSION_MAJOR}}
|
||||
#define LIBOSMO_MGCP_CLIENT_VERSION_MINOR {{VERSION_MINOR}}
|
||||
#define LIBOSMO_MGCP_CLIENT_VERSION_PATCH {{VERSION_PATCH}}
|
||||
|
||||
#define LIBOSMO_MGCP_CLIENT_VERSION_GREATER_EQUAL(major, minor, patch) \
|
||||
(LIBOSMO_MGCP_CLIENT_VERSION_MAJOR > (major) || \
|
||||
(LIBOSMO_MGCP_CLIENT_VERSION_MAJOR == (major) && \
|
||||
LIBOSMO_MGCP_CLIENT_VERSION_MINOR > (minor)) || \
|
||||
(LIBOSMO_MGCP_CLIENT_VERSION_MAJOR == (major) && \
|
||||
LIBOSMO_MGCP_CLIENT_VERSION_MINOR == (minor) && \
|
||||
LIBOSMO_MGCP_CLIENT_VERSION_PATCH >= (patch)))
|
||||
@@ -19,7 +19,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=14:0:0
|
||||
MGCP_CLIENT_LIBVERSION=14:1:0
|
||||
|
||||
lib_LTLIBRARIES = \
|
||||
libosmo-mgcp-client.la \
|
||||
|
||||
@@ -786,8 +786,10 @@ static void mgcp_read_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg)
|
||||
struct mgcp_client *mgcp = osmo_iofd_get_data(iofd);
|
||||
|
||||
if (res <= 0) {
|
||||
char errbuf[128] = "";
|
||||
strerror_r(-res, errbuf, sizeof(errbuf));
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Failed to read: %s: %d='%s'\n",
|
||||
osmo_iofd_get_name(iofd), res, strerror(res));
|
||||
osmo_iofd_get_name(iofd), res, errbuf);
|
||||
|
||||
msgb_free(msg);
|
||||
return;
|
||||
@@ -822,8 +824,10 @@ static void mgcp_write_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg)
|
||||
struct mgcp_client *mgcp = osmo_iofd_get_data(iofd);
|
||||
|
||||
if (OSMO_UNLIKELY(res != msg->len)) {
|
||||
char errbuf[128] = "";
|
||||
strerror_r(-res, errbuf, sizeof(errbuf));
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Failed to Tx MGCP: %s: %d='%s'; msg: len=%u '%s'...\n",
|
||||
osmo_iofd_get_name(mgcp->iofd), res, strerror(res),
|
||||
osmo_iofd_get_name(mgcp->iofd), res, errbuf,
|
||||
msg->len, osmo_escape_str((const char *)msg->data, OSMO_MIN(42, msg->len)));
|
||||
}
|
||||
}
|
||||
@@ -860,6 +864,10 @@ static void _mgcp_client_send_dlcx(struct mgcp_client *mgcp, const char *epname)
|
||||
};
|
||||
osmo_strlcpy(mgcp_msg_dlcx.endpoint, epname, sizeof(mgcp_msg_dlcx.endpoint));
|
||||
msgb_dlcx = mgcp_msg_gen(mgcp, &mgcp_msg_dlcx);
|
||||
if (!msgb_dlcx) {
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Failed generating MGCP DLCX %s\n", epname);
|
||||
return;
|
||||
}
|
||||
mgcp_client_tx(mgcp, msgb_dlcx, &_ignore_mgcp_response, NULL);
|
||||
}
|
||||
|
||||
@@ -873,6 +881,10 @@ static void _mgcp_client_send_auep(struct mgcp_client *mgcp, const char *epname)
|
||||
};
|
||||
OSMO_STRLCPY_ARRAY(mgcp_msg_auep.endpoint, epname);
|
||||
msgb_auep = mgcp_msg_gen(mgcp, &mgcp_msg_auep);
|
||||
if (!msgb_auep) {
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Failed generating MGCP AUEP %s\n", epname);
|
||||
return;
|
||||
}
|
||||
mgcp_client_tx(mgcp, msgb_auep, &_ignore_mgcp_response, NULL);
|
||||
}
|
||||
|
||||
@@ -1047,6 +1059,7 @@ void mgcp_client_disconnect(struct mgcp_client *mgcp)
|
||||
osmo_iofd_txqueue_clear(mgcp->iofd);
|
||||
LOGPMGW(mgcp, LOGL_INFO, "MGCP association: %s -- closed!\n", osmo_iofd_get_name(mgcp->iofd));
|
||||
osmo_iofd_free(mgcp->iofd);
|
||||
mgcp->iofd = NULL;
|
||||
}
|
||||
|
||||
/*! Get the IP-Aaddress of the associated MGW as string.
|
||||
@@ -1307,6 +1320,36 @@ static int add_lco(struct msgb *msg, struct mgcp_msg *mgcp_msg)
|
||||
#undef MSGB_PRINTF_OR_RET
|
||||
}
|
||||
|
||||
/* Helper function to obtain local IP address used in MGCP towards MGW,
|
||||
* in string format, to fill the SDP "Origin" ("o=") field.
|
||||
* return 0 on success, negative on error.
|
||||
*/
|
||||
static int get_mgcp_local_addr(const struct mgcp_client *mgcp, char *local_ip, size_t local_ip_len)
|
||||
{
|
||||
int fd;
|
||||
|
||||
/* Try to get the socket local IP address if available: */
|
||||
if (mgcp->iofd && ((fd = osmo_iofd_get_fd(mgcp->iofd)) >= 0)) {
|
||||
if (osmo_sock_get_local_ip(fd, local_ip, local_ip_len) == 0)
|
||||
return 0;
|
||||
/* else: continue below */
|
||||
}
|
||||
|
||||
/* If MGCP local address was explicitly specified in config, use it: */
|
||||
if (mgcp->actual.local_addr) {
|
||||
osmo_strlcpy(local_ip, mgcp->actual.local_addr, local_ip_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Guess our local address based on system routing towards MGW: */
|
||||
OSMO_ASSERT(local_ip_len >= INET6_ADDRSTRLEN);
|
||||
if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) == 0)
|
||||
return 0;
|
||||
|
||||
LOGPMGW(mgcp, LOGL_ERROR, "Could not determine local IP-Address!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
@@ -1316,6 +1359,7 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
|
||||
const char *codec;
|
||||
unsigned int pt;
|
||||
uint16_t audio_port;
|
||||
int rc;
|
||||
|
||||
#define MSGB_PRINTF_OR_RET(FMT, ARGS...) do { \
|
||||
if (msgb_printf(msg, FMT, ##ARGS) != 0) { \
|
||||
@@ -1331,11 +1375,9 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
|
||||
MSGB_PRINTF_OR_RET("v=0\r\n");
|
||||
|
||||
/* Determine local IP-Address */
|
||||
if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) < 0) {
|
||||
LOGPMGW(mgcp, LOGL_ERROR,
|
||||
"Could not determine local IP-Address!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
rc = get_mgcp_local_addr(mgcp, local_ip, sizeof(local_ip));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
local_ip_family = osmo_ip_str_type(local_ip);
|
||||
if (local_ip_family == AF_UNSPEC)
|
||||
return -EINVAL;
|
||||
@@ -1384,15 +1426,32 @@ static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_clie
|
||||
/* Add optional codec parameters (fmtp) */
|
||||
if (mgcp_msg->param_present) {
|
||||
for (i = 0; i < mgcp_msg->ptmap_len; i++) {
|
||||
/* The following is only applicable for AMR */
|
||||
if (mgcp_msg->ptmap[i].codec != CODEC_AMR_8000_1
|
||||
&& mgcp_msg->ptmap[i].codec != CODEC_AMRWB_16000_1)
|
||||
continue;
|
||||
pt = mgcp_msg->ptmap[i].pt;
|
||||
if (mgcp_msg->param.amr_octet_aligned_present && mgcp_msg->param.amr_octet_aligned)
|
||||
MSGB_PRINTF_OR_RET("a=fmtp:%u octet-align=1\r\n", pt);
|
||||
else if (mgcp_msg->param.amr_octet_aligned_present && !mgcp_msg->param.amr_octet_aligned)
|
||||
MSGB_PRINTF_OR_RET("a=fmtp:%u octet-align=0\r\n", pt);
|
||||
switch (mgcp_msg->ptmap[i].codec) {
|
||||
case CODEC_AMR_8000_1:
|
||||
case CODEC_AMRWB_16000_1:
|
||||
if (!mgcp_msg->param.amr_octet_aligned_present)
|
||||
break;
|
||||
MSGB_PRINTF_OR_RET("a=fmtp:%u octet-align=%d\r\n",
|
||||
pt, (int)mgcp_msg->param.amr_octet_aligned);
|
||||
break;
|
||||
case CODEC_GSM_8000_1:
|
||||
case CODEC_GSMEFR_8000_1:
|
||||
if (!mgcp_msg->param.fr_efr_twts001_present)
|
||||
break;
|
||||
MSGB_PRINTF_OR_RET("a=fmtp:%u tw-ts-001=%d\r\n",
|
||||
pt, (int)mgcp_msg->param.fr_efr_twts001);
|
||||
break;
|
||||
case CODEC_GSMHR_8000_1:
|
||||
if (!mgcp_msg->param.hr_twts002_present)
|
||||
break;
|
||||
MSGB_PRINTF_OR_RET("a=fmtp:%u tw-ts-002=%d\r\n",
|
||||
pt, (int)mgcp_msg->param.hr_twts002);
|
||||
break;
|
||||
default:
|
||||
/* no parameters for the remaining codecs */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -157,6 +157,7 @@ static void set_conn_mode(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *peer
|
||||
mgcp_msg->conn_mode = conn_mode;
|
||||
}
|
||||
|
||||
/* returns message buffer containing MGXP MDCX on success, NULL on error. */
|
||||
static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
|
||||
{
|
||||
struct mgcp_msg mgcp_msg;
|
||||
@@ -192,6 +193,7 @@ static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
|
||||
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
|
||||
}
|
||||
|
||||
/* returns message buffer containing MGXP DLCX on success, NULL on error. */
|
||||
struct msgb *make_dlcx_msg(struct mgcp_ctx *mgcp_ctx)
|
||||
{
|
||||
struct mgcp_msg mgcp_msg;
|
||||
@@ -231,8 +233,10 @@ static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
set_conn_mode(&mgcp_msg, &mgcp_ctx->conn_peer_local);
|
||||
|
||||
msg = mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
|
||||
OSMO_ASSERT(msg);
|
||||
|
||||
if (!msg) {
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return;
|
||||
}
|
||||
mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
|
||||
mgcp_ctx->mgw_trans_pending = true;
|
||||
rc = mgcp_client_tx(mgcp, msg, mgw_crcx_resp_cb, fi);
|
||||
|
||||
@@ -7,6 +7,7 @@ AM_CPPFLAGS = \
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOCODEC_CFLAGS) \
|
||||
$(LIBOSMOGSM_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMONETIF_CFLAGS) \
|
||||
@@ -40,6 +41,7 @@ libosmo_mgcp_a_SOURCES = \
|
||||
mgcp_endp.c \
|
||||
mgcp_trunk.c \
|
||||
mgcp_ratectr.c \
|
||||
mgcp_rtp_end.c \
|
||||
mgcp_e1.c \
|
||||
mgcp_iuup.c \
|
||||
$(NULL)
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
/* 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 *mgcp_codec_dump(struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
static char str[256];
|
||||
char *pt_str;
|
||||
@@ -49,31 +49,25 @@ static char *dump_codec(struct mgcp_rtp_codec *codec)
|
||||
}
|
||||
|
||||
/*! 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)
|
||||
* \param[in] cset related codecset.
|
||||
* \param[in] prefix_str Prefix string to print during logging.
|
||||
* */
|
||||
void mgcp_codecset_summary(struct mgcp_rtp_codecset *cset, const char *prefix_str)
|
||||
{
|
||||
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) {
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "conn:%s no codecs available\n",
|
||||
mgcp_conn_dump(conn->conn));
|
||||
if (cset->codecs_assigned == 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "%s no codecs available\n", prefix_str);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Store parsed codec information */
|
||||
for (i = 0; i < rtp->codecs_assigned; i++) {
|
||||
codec = &rtp->codecs[i];
|
||||
for (i = 0; i < cset->codecs_assigned; i++) {
|
||||
struct mgcp_rtp_codec *codec = &cset->codecs[i];
|
||||
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s codecs[%u]:%s",
|
||||
mgcp_conn_dump(conn->conn), i, dump_codec(codec));
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "%s codecs[%u]:%s", prefix_str, i, mgcp_codec_dump(codec));
|
||||
|
||||
if (codec == rtp->codec)
|
||||
if (codec == cset->codec)
|
||||
LOGPC(DLMGCP, LOGL_DEBUG, " [selected]");
|
||||
|
||||
LOGPC(DLMGCP, LOGL_DEBUG, "\n");
|
||||
@@ -81,7 +75,7 @@ void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
|
||||
}
|
||||
|
||||
/* Initalize or reset codec information with default data. */
|
||||
static void codec_init(struct mgcp_rtp_codec *codec)
|
||||
static void mgcp_codec_init(struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
*codec = (struct mgcp_rtp_codec){
|
||||
.payload_type = -1,
|
||||
@@ -94,20 +88,20 @@ static void codec_init(struct mgcp_rtp_codec *codec)
|
||||
};
|
||||
}
|
||||
|
||||
static void codec_free(struct mgcp_rtp_codec *codec)
|
||||
static void mgcp_codec_free(struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
*codec = (struct mgcp_rtp_codec){};
|
||||
}
|
||||
|
||||
/*! Initalize or reset codec information with default data.
|
||||
* \param[out] conn related rtp-connection. */
|
||||
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
|
||||
void mgcp_codecset_reset(struct mgcp_rtp_codecset *cset)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < conn->end.codecs_assigned; i++)
|
||||
codec_free(&conn->end.codecs[i]);
|
||||
conn->end.codecs_assigned = 0;
|
||||
conn->end.codec = NULL;
|
||||
for (i = 0; i < cset->codecs_assigned; i++)
|
||||
mgcp_codec_free(&cset->codecs[i]);
|
||||
cset->codecs_assigned = 0;
|
||||
cset->codec = NULL;
|
||||
}
|
||||
|
||||
/*! Add codec configuration depending on payload type and/or codec name. This
|
||||
@@ -118,23 +112,23 @@ void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
|
||||
* \param[in] audio_name audio codec name, in uppercase (e.g. "GSM/8000/1").
|
||||
* \param[in] param optional codec parameters (set to NULL when unused).
|
||||
* \returns 0 on success, -EINVAL on failure. */
|
||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
|
||||
int mgcp_codecset_add_codec(struct mgcp_rtp_codecset *cset, int payload_type, const char *audio_name, const struct mgcp_codec_param *param)
|
||||
{
|
||||
int rate;
|
||||
int channels;
|
||||
struct mgcp_rtp_codec *codec;
|
||||
unsigned int pt_offset = conn->end.codecs_assigned;
|
||||
unsigned int pt_offset = cset->codecs_assigned;
|
||||
|
||||
/* 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)
|
||||
if (cset->codecs_assigned >= MGCP_MAX_CODECS)
|
||||
return -EINVAL;
|
||||
|
||||
/* First unused entry */
|
||||
codec = &conn->end.codecs[conn->end.codecs_assigned];
|
||||
codec = &cset->codecs[cset->codecs_assigned];
|
||||
|
||||
/* Initalize the codec struct with some default data to begin with */
|
||||
codec_init(codec);
|
||||
mgcp_codec_init(codec);
|
||||
|
||||
if (payload_type != PTYPE_UNDEFINED) {
|
||||
/* Make sure we do not get any reserved or undefined type numbers */
|
||||
@@ -268,11 +262,11 @@ int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *aud
|
||||
} else
|
||||
codec->param_present = false;
|
||||
|
||||
conn->end.codecs_assigned++;
|
||||
cset->codecs_assigned++;
|
||||
return 0;
|
||||
error:
|
||||
/* Make sure we leave a clean codec entry on error. */
|
||||
codec_free(codec);
|
||||
mgcp_codec_free(codec);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -296,7 +290,7 @@ bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec)
|
||||
}
|
||||
|
||||
/* Compare two codecs, all parameters must match up */
|
||||
static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b)
|
||||
static bool codecs_same(const struct mgcp_rtp_codec *codec_a, const struct mgcp_rtp_codec *codec_b)
|
||||
{
|
||||
/* All codec properties must match up, except the payload type number. Even though standardisd payload numbers
|
||||
* exist for certain situations, the call agent may still assign them freely. Hence we must not insist on equal
|
||||
@@ -325,7 +319,7 @@ static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *c
|
||||
}
|
||||
|
||||
/* Compare two codecs, all parameters must match up, except parameters related to payload formatting (not checked). */
|
||||
static bool codecs_convertible(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b)
|
||||
static bool codecs_convertible(const struct mgcp_rtp_codec *codec_a, const struct mgcp_rtp_codec *codec_b)
|
||||
{
|
||||
/* OsmoMGW currently has no ability to transcode from one codec to another. However OsmoMGW is still able to
|
||||
* translate between different payload formats as long as the encoded voice data itself does not change.
|
||||
@@ -354,21 +348,18 @@ iufp:
|
||||
return true;
|
||||
}
|
||||
|
||||
struct mgcp_rtp_codec *mgcp_codec_find_same(struct mgcp_conn_rtp *conn, struct mgcp_rtp_codec *codec)
|
||||
struct mgcp_rtp_codec *mgcp_codecset_find_same(struct mgcp_rtp_codecset *cset, const struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
struct mgcp_rtp_end *rtp_end;
|
||||
unsigned int i;
|
||||
unsigned int codecs_assigned;
|
||||
|
||||
rtp_end = &conn->end;
|
||||
|
||||
/* Use the codec information from the source and try to find the equivalent of it on the destination side. In
|
||||
* the first run we will look for an exact match. */
|
||||
codecs_assigned = rtp_end->codecs_assigned;
|
||||
codecs_assigned = cset->codecs_assigned;
|
||||
OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
|
||||
for (i = 0; i < codecs_assigned; i++) {
|
||||
if (codecs_same(codec, &rtp_end->codecs[i])) {
|
||||
return &rtp_end->codecs[i];
|
||||
if (codecs_same(codec, &cset->codecs[i])) {
|
||||
return &cset->codecs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -377,28 +368,26 @@ struct mgcp_rtp_codec *mgcp_codec_find_same(struct mgcp_conn_rtp *conn, struct m
|
||||
}
|
||||
|
||||
/* For a given codec, find a convertible codec in the given connection. */
|
||||
static struct mgcp_rtp_codec *codec_find_convertible(struct mgcp_conn_rtp *conn, struct mgcp_rtp_codec *codec)
|
||||
static struct mgcp_rtp_codec *codecset_find_convertible(struct mgcp_rtp_codecset *cset, const struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
struct mgcp_rtp_end *rtp_end;
|
||||
unsigned int i;
|
||||
unsigned int codecs_assigned;
|
||||
struct mgcp_rtp_codec *codec_convertible = NULL;
|
||||
|
||||
rtp_end = &conn->end;
|
||||
|
||||
/* Use the codec information from the source and try to find the equivalent of it on the destination side. In
|
||||
* the first run we will look for an exact match. */
|
||||
codec_convertible = mgcp_codec_find_same(conn, codec);
|
||||
codec_convertible = mgcp_codecset_find_same(cset, codec);
|
||||
if (codec_convertible)
|
||||
return codec_convertible;
|
||||
|
||||
/* In case we weren't able to find an exact match, we will try to find a match that is the same codec, but the
|
||||
* payload format may be different. This alternative will require a frame format conversion (i.e. AMR bwe->oe) */
|
||||
codecs_assigned = rtp_end->codecs_assigned;
|
||||
codecs_assigned = cset->codecs_assigned;
|
||||
OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
|
||||
for (i = 0; i < codecs_assigned; i++) {
|
||||
if (codecs_convertible(codec, &rtp_end->codecs[i])) {
|
||||
codec_convertible = &rtp_end->codecs[i];
|
||||
if (codecs_convertible(codec, &cset->codecs[i])) {
|
||||
codec_convertible = &cset->codecs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -408,20 +397,20 @@ static struct mgcp_rtp_codec *codec_find_convertible(struct mgcp_conn_rtp *conn,
|
||||
|
||||
/*! Decide for one suitable codec on both of the given connections. In case a destination connection is not available,
|
||||
* a tentative decision is made.
|
||||
* \param[inout] conn_src related rtp-connection.
|
||||
* \param[inout] conn_dst related destination rtp-connection (NULL if not present).
|
||||
* \param[in] cset_src related codec set.
|
||||
* \param[inout] cset_dst related destination codec set (NULL if not present).
|
||||
* \returns 0 on success, -EINVAL on failure. */
|
||||
int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst)
|
||||
int mgcp_codecset_decide(struct mgcp_rtp_codecset *cset_src, struct mgcp_rtp_codecset *cset_dst)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/* In case no destination connection is available (yet), or in case the destination connection exists but has
|
||||
* no codecs assigned, we are forced to make a simple tentative decision:
|
||||
* We just use the first codec of the source connection (conn_src) */
|
||||
OSMO_ASSERT(conn_src->end.codecs_assigned <= MGCP_MAX_CODECS);
|
||||
if (!conn_dst || conn_dst->end.codecs_assigned == 0) {
|
||||
if (conn_src->end.codecs_assigned >= 1) {
|
||||
conn_src->end.codec = &conn_src->end.codecs[0];
|
||||
OSMO_ASSERT(cset_src->codecs_assigned <= MGCP_MAX_CODECS);
|
||||
if (!cset_dst || cset_dst->codecs_assigned == 0) {
|
||||
if (cset_src->codecs_assigned >= 1) {
|
||||
cset_src->codec = &cset_src->codecs[0];
|
||||
return 0;
|
||||
} else
|
||||
return -EINVAL;
|
||||
@@ -430,38 +419,38 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn
|
||||
/* Compare all codecs of the source connection (conn_src) to the codecs of the destination connection (conn_dst). In case
|
||||
* of a match set this codec on both connections. This would be an ideal selection since no codec conversion would be
|
||||
* required. */
|
||||
for (i = 0; i < conn_src->end.codecs_assigned; i++) {
|
||||
struct mgcp_rtp_codec *codec_conn_src = &conn_src->end.codecs[i];
|
||||
struct mgcp_rtp_codec *codec_conn_dst = mgcp_codec_find_same(conn_dst, codec_conn_src);
|
||||
if (codec_conn_dst) {
|
||||
for (i = 0; i < cset_src->codecs_assigned; i++) {
|
||||
struct mgcp_rtp_codec *codec_cset_src = &cset_src->codecs[i];
|
||||
struct mgcp_rtp_codec *codec_cset_dst = mgcp_codecset_find_same(cset_dst, codec_cset_src);
|
||||
if (codec_cset_dst) {
|
||||
/* We found the a codec that is exactly the same (same codec, same payload format etc.) on both
|
||||
* sides. We now set this codec on both connections. */
|
||||
conn_dst->end.codec = codec_conn_dst;
|
||||
conn_src->end.codec = codec_conn_src;
|
||||
cset_dst->codec = codec_cset_dst;
|
||||
cset_src->codec = codec_cset_src;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* In case we could not find a codec that is exactly the same, let's at least try to find a codec that we are able
|
||||
* to convert. */
|
||||
for (i = 0; i < conn_src->end.codecs_assigned; i++) {
|
||||
struct mgcp_rtp_codec *codec_conn_src = &conn_src->end.codecs[i];
|
||||
struct mgcp_rtp_codec *codec_conn_dst = codec_find_convertible(conn_dst, codec_conn_src);
|
||||
if (codec_conn_dst) {
|
||||
for (i = 0; i < cset_src->codecs_assigned; i++) {
|
||||
struct mgcp_rtp_codec *codec_cset_src = &cset_src->codecs[i];
|
||||
struct mgcp_rtp_codec *codec_cset_dst = codecset_find_convertible(cset_dst, codec_cset_src);
|
||||
if (codec_cset_dst) {
|
||||
/* We found the a codec that we can convert to. Set each side to its codec. */
|
||||
conn_dst->end.codec = codec_conn_dst;
|
||||
conn_src->end.codec = codec_conn_src;
|
||||
cset_dst->codec = codec_cset_dst;
|
||||
cset_src->codec = codec_cset_src;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (conn_dst->end.codecs_assigned)
|
||||
conn_dst->end.codec = &conn_dst->end.codecs[0];
|
||||
if (cset_dst->codecs_assigned)
|
||||
cset_dst->codec = &cset_dst->codecs[0];
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
if (conn_src->end.codecs_assigned)
|
||||
conn_src->end.codec = &conn_src->end.codecs[0];
|
||||
if (cset_src->codecs_assigned)
|
||||
cset_src->codec = &cset_src->codecs[0];
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
@@ -488,38 +477,36 @@ bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec)
|
||||
* \param match_nr Index for the match found, first being match_nr == 0. Iterate all matches by calling multiple times
|
||||
* with incrementing match_nr.
|
||||
* \return codec definition for that conn matching the subtype_name, or NULL if no such match_nr is found. */
|
||||
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
|
||||
const struct mgcp_rtp_codec *mgcp_codecset_pt_find_by_subtype_name(const struct mgcp_rtp_codecset *cset,
|
||||
const char *subtype_name, unsigned int match_nr)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < conn->end.codecs_assigned; i++) {
|
||||
if (!strcmp(conn->end.codecs[i].subtype_name, subtype_name)) {
|
||||
for (i = 0; i < cset->codecs_assigned; i++) {
|
||||
if (!strcmp(cset->codecs[i].subtype_name, subtype_name)) {
|
||||
if (match_nr) {
|
||||
match_nr--;
|
||||
continue;
|
||||
}
|
||||
return &conn->end.codecs[i];
|
||||
return &cset->codecs[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Lookup a codec that is assigned to a connection by its payload type number.
|
||||
* \param[in] conn related rtp-connection.
|
||||
* \param[in] cset related codec set.
|
||||
* \param[in] payload_type number of the codec to look up.
|
||||
* \returns pointer to codec struct on success, NULL on failure. */
|
||||
struct mgcp_rtp_codec *mgcp_codec_from_pt(struct mgcp_conn_rtp *conn, int payload_type)
|
||||
struct mgcp_rtp_codec *mgcp_codecset_find_codec_from_pt(struct mgcp_rtp_codecset *cset, int payload_type)
|
||||
{
|
||||
struct mgcp_rtp_end *rtp_end = &conn->end;
|
||||
unsigned int codecs_assigned = rtp_end->codecs_assigned;
|
||||
struct mgcp_rtp_codec *codec = NULL;
|
||||
size_t i;
|
||||
|
||||
OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);
|
||||
OSMO_ASSERT(cset->codecs_assigned <= MGCP_MAX_CODECS);
|
||||
|
||||
for (i = 0; i < codecs_assigned; i++) {
|
||||
if (payload_type == rtp_end->codecs[i].payload_type) {
|
||||
codec = &rtp_end->codecs[i];
|
||||
for (i = 0; i < cset->codecs_assigned; i++) {
|
||||
if (payload_type == cset->codecs[i].payload_type) {
|
||||
codec = &cset->codecs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
|
||||
|
||||
/* ensure that the generated conn_id is unique
|
||||
* for this endpoint */
|
||||
if (!mgcp_conn_get_rtp(endp, id_hex)) {
|
||||
if (!mgcp_endp_get_conn_rtp(endp, id_hex)) {
|
||||
osmo_strlcpy(id, id_hex, MGCP_CONN_ID_MAXLEN);
|
||||
return 0;
|
||||
}
|
||||
@@ -88,7 +88,6 @@ static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
|
||||
/* Initialize rtp connection struct with default values */
|
||||
static int 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. */
|
||||
@@ -106,17 +105,6 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *
|
||||
/* backpointer to the generic part of the connection */
|
||||
conn->u.rtp.conn = conn;
|
||||
|
||||
end->rtp = NULL;
|
||||
end->rtcp = NULL;
|
||||
memset(&end->addr, 0, sizeof(end->addr));
|
||||
end->rtcp_port = 0;
|
||||
|
||||
/* Set default values */
|
||||
end->frames_per_packet = 0; /* unknown */
|
||||
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
|
||||
end->output_enabled = false;
|
||||
end->maximum_packet_time = -1;
|
||||
|
||||
conn_rtp->ctrg = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index++);
|
||||
if (!conn_rtp->ctrg)
|
||||
return -1;
|
||||
@@ -124,9 +112,7 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *
|
||||
conn_rtp->state.in_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->ctrg, IN_STREAM_ERR_TSTMP_CTR);
|
||||
conn_rtp->state.out_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->ctrg, OUT_STREAM_ERR_TSTMP_CTR);
|
||||
|
||||
/* Make sure codec table is reset */
|
||||
mgcp_codec_reset_all(conn_rtp);
|
||||
|
||||
mgcp_rtp_end_init(&conn_rtp->end, conn_rtp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -137,16 +123,15 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
|
||||
conn_osmux_disable(conn_rtp);
|
||||
if (mgcp_conn_rtp_is_iuup(conn_rtp))
|
||||
mgcp_conn_iuup_cleanup(conn_rtp);
|
||||
mgcp_free_rtp_port(&conn_rtp->end);
|
||||
mgcp_rtp_end_cleanup(&conn_rtp->end);
|
||||
rate_ctr_group_free(conn_rtp->ctrg);
|
||||
mgcp_codec_reset_all(conn_rtp);
|
||||
}
|
||||
|
||||
void mgcp_conn_watchdog_cb(void *data)
|
||||
{
|
||||
struct mgcp_conn *conn = data;
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR, "connection timed out!\n");
|
||||
mgcp_conn_free(conn->endp, conn->id);
|
||||
mgcp_conn_free(conn);
|
||||
}
|
||||
|
||||
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn)
|
||||
@@ -212,58 +197,6 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
|
||||
return conn;
|
||||
}
|
||||
|
||||
/*! find a connection by its ID.
|
||||
* \param[in] endp associated endpoint
|
||||
* \param[in] id identification number of the connection
|
||||
* \returns pointer to allocated connection, NULL if not found */
|
||||
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
const char *id_upper;
|
||||
const char *conn_id;
|
||||
|
||||
if (!id || !*id)
|
||||
return NULL;
|
||||
|
||||
/* Ignore leading zeros in needle */
|
||||
while (*id == '0')
|
||||
id++;
|
||||
|
||||
/* Use uppercase to compare identifiers, to avoid mismatches: RFC3435 2.1.3.2 "Names of
|
||||
* Connections" defines the id as a hex string, so clients may return lower case hex even though
|
||||
* we sent upper case hex in the CRCX response. */
|
||||
id_upper = osmo_str_toupper(id);
|
||||
|
||||
llist_for_each_entry(conn, &endp->conns, entry) {
|
||||
/* Ignore leading zeros in haystack */
|
||||
for (conn_id=conn->id; *conn_id == '0'; conn_id++);
|
||||
|
||||
if (strcmp(conn_id, id_upper) == 0)
|
||||
return conn;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! find an RTP connection by its ID.
|
||||
* \param[in] endp associated endpoint
|
||||
* \param[in] id identification number of the connection
|
||||
* \returns pointer to allocated connection, NULL if not found */
|
||||
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
|
||||
const char *id)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
conn = mgcp_conn_get(endp, id);
|
||||
if (!conn)
|
||||
return NULL;
|
||||
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP)
|
||||
return &conn->u.rtp;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn_rtp)
|
||||
{
|
||||
struct rate_ctr_group *all_stats = endp->trunk->ratectr.all_rtp_conn_stats;
|
||||
@@ -286,21 +219,21 @@ static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_con
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(all_stats, RTP_NUM_CONNECTIONS));
|
||||
}
|
||||
|
||||
/*! free a connection by its ID.
|
||||
* \param[in] endp associated endpoint
|
||||
* \param[in] id identification number of the connection */
|
||||
void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
|
||||
/*! free a connection
|
||||
* \param[in] conn the conn to free. May be NULL.
|
||||
*/
|
||||
void mgcp_conn_free(struct mgcp_conn *conn)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
struct mgcp_conn_rtp *conn_rtp;
|
||||
|
||||
conn = mgcp_conn_get(endp, id);
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
switch (conn->type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
aggregate_rtp_conn_stats(endp, &conn->u.rtp);
|
||||
mgcp_rtp_conn_cleanup(&conn->u.rtp);
|
||||
conn_rtp = mgcp_conn_get_conn_rtp(conn);
|
||||
aggregate_rtp_conn_stats(conn->endp, conn_rtp);
|
||||
mgcp_rtp_conn_cleanup(conn_rtp);
|
||||
break;
|
||||
default:
|
||||
/* NOTE: This should never be called with an
|
||||
@@ -310,43 +243,40 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
|
||||
}
|
||||
|
||||
osmo_timer_del(&conn->watchdog);
|
||||
mgcp_endp_remove_conn(endp, conn);
|
||||
mgcp_endp_remove_conn(conn->endp, conn);
|
||||
/* WARN: endp may have be freed after call to mgcp_endp_remove_conn */
|
||||
talloc_free(conn);
|
||||
}
|
||||
|
||||
/*! free oldest connection in the list.
|
||||
* \param[in] endp associated endpoint */
|
||||
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp)
|
||||
/*! Parse connection mode.
|
||||
* \param[in] conn Connection whose mode is being set
|
||||
* \param[in] mode Mode to set
|
||||
* \returns 0 on success, -1 on error */
|
||||
int mgcp_conn_set_mode(struct mgcp_conn *conn, enum mgcp_connection_mode mode)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
OSMO_ASSERT(conn);
|
||||
if (mode == MGCP_CONN_NONE) {
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
|
||||
"missing connection mode\n");
|
||||
return -1;
|
||||
}
|
||||
conn->mode = mode;
|
||||
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "connection mode '%s' %d\n",
|
||||
mgcp_cmode_name(mode), conn->mode);
|
||||
|
||||
if (llist_empty(&endp->conns))
|
||||
return;
|
||||
/* Special handling for RTP connections */
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
|
||||
conn_rtp->end.output_enabled = !!(conn->mode & MGCP_CONN_SEND_ONLY);
|
||||
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %u\n",
|
||||
conn_rtp->end.output_enabled);
|
||||
}
|
||||
|
||||
conn = llist_last_entry(&endp->conns, struct mgcp_conn, entry);
|
||||
if (!conn)
|
||||
return;
|
||||
/* The VTY might change the connection mode at any time, so we have
|
||||
* to hold a copy of the original connection mode */
|
||||
conn->mode_orig = conn->mode;
|
||||
|
||||
mgcp_conn_free(endp, conn->id);
|
||||
}
|
||||
|
||||
/*! free all connections at once.
|
||||
* \param[in] endp associated endpoint */
|
||||
#if defined(__has_attribute)
|
||||
#if __has_attribute(no_sanitize)
|
||||
__attribute__((no_sanitize("undefined"))) /* ubsan detects a misaligned load */
|
||||
#endif
|
||||
#endif
|
||||
void mgcp_conn_free_all(struct mgcp_endpoint *endp)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
/* Drop all items in the list, might be consecutive! */
|
||||
while ((conn = llist_first_entry_or_null(&endp->conns, struct mgcp_conn, entry)))
|
||||
mgcp_conn_free(endp, conn->id);
|
||||
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! dump basic connection information to human readable string.
|
||||
@@ -357,24 +287,26 @@ char *mgcp_conn_dump(struct mgcp_conn *conn)
|
||||
static char str[sizeof(conn->name)+sizeof(conn->id)+256];
|
||||
char ipbuf[INET6_ADDRSTRLEN];
|
||||
struct osmo_strbuf sb = { .buf = str, .len = sizeof(str) };
|
||||
struct mgcp_conn_rtp *conn_rtp;
|
||||
|
||||
if (!conn)
|
||||
return "NULL";
|
||||
|
||||
switch (conn->type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
conn_rtp = mgcp_conn_get_conn_rtp(conn);
|
||||
OSMO_STRBUF_PRINTF(sb, "(%s/%s C:%s r=%s:%u<->l=%s:%u",
|
||||
conn->name,
|
||||
mgcp_conn_rtp_type_name(conn->type),
|
||||
conn->id,
|
||||
osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf) ? : "NULL",
|
||||
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa),
|
||||
conn->u.rtp.end.local_addr ? : "NULL",
|
||||
conn->u.rtp.end.local_port);
|
||||
osmo_sockaddr_ntop(&conn_rtp->end.addr.u.sa, ipbuf) ? : "NULL",
|
||||
osmo_sockaddr_port(&conn_rtp->end.addr.u.sa),
|
||||
conn_rtp->end.local_addr ? : "NULL",
|
||||
conn_rtp->end.local_port);
|
||||
|
||||
switch (conn->u.rtp.type) {
|
||||
switch (conn_rtp->type) {
|
||||
case MGCP_RTP_OSMUX:
|
||||
OSMO_STRBUF_PRINTF(sb, " CID=%u", conn->u.rtp.osmux.local_cid);
|
||||
OSMO_STRBUF_PRINTF(sb, " CID=%u", conn_rtp->osmux.local_cid);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -414,16 +346,6 @@ struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! get oldest connection in the list.
|
||||
* \param[in] endp associated endpoint */
|
||||
struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp)
|
||||
{
|
||||
if (llist_empty(&endp->conns))
|
||||
return NULL;
|
||||
|
||||
return llist_last_entry(&endp->conns, struct mgcp_conn, entry);
|
||||
}
|
||||
|
||||
const struct value_string mgcp_conn_rtp_type_names[] = {
|
||||
{ MGCP_RTP_DEFAULT, "rtp" },
|
||||
{ MGCP_RTP_OSMUX, "osmux" },
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <osmocom/mgcp/debug.h>
|
||||
#include <osmocom/mgcp/mgcp_e1.h>
|
||||
#include <osmocom/codec/codec.h>
|
||||
#include <osmocom/gsm/rtp_extensions.h>
|
||||
|
||||
#define DEBUG_BITS_MAX 80
|
||||
#define DEBUG_BYTES_MAX 40
|
||||
@@ -56,135 +57,6 @@ static const struct e1inp_line_ops dummy_e1_line_ops = {
|
||||
.sign_link = NULL,
|
||||
};
|
||||
|
||||
/* EFR idle frame */
|
||||
static const ubit_t idle_tf_efr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
};
|
||||
|
||||
/* FR idle frame */
|
||||
static const ubit_t idle_tf_fr[] = { 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
};
|
||||
|
||||
/* Idle speech frame, see also GSM 08.60, chapter 3.4 */
|
||||
static const ubit_t idle_tf_spch[] = { 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 1, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
};
|
||||
|
||||
/* If the RTP transmission has dropouts for some reason the I.460 TX-Queue may
|
||||
* run empty. In order to make sure that the TRAU frame transmission continues
|
||||
* we generate idle TRAU frames here. */
|
||||
@@ -193,33 +65,59 @@ static void e1_i460_mux_empty_cb(struct osmo_i460_subchan *schan, void *user_dat
|
||||
struct mgcp_endpoint *endp = user_data;
|
||||
struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats;
|
||||
struct msgb *msg = msgb_alloc_c(endp->trunk, E1_TRAU_BITS_MSGB, "E1-I.460-IDLE-TX-TRAU-frame");
|
||||
uint8_t *ptr;
|
||||
const uint8_t *ptr_ft;
|
||||
enum osmo_trau_frame_type ft;
|
||||
const uint8_t *dummy_fill_pl;
|
||||
unsigned dummy_fill_pl_len;
|
||||
struct osmo_trau_frame tf;
|
||||
int rc;
|
||||
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_MUX_EMPTY_CTR));
|
||||
|
||||
/* Choose an appropiate idle frame type */
|
||||
ft = endp->e1.trau_rtp_st->type;
|
||||
switch (ft) {
|
||||
/* choose dummy fill frame payload based on current codec */
|
||||
switch (endp->e1.trau_rtp_st->type) {
|
||||
case OSMO_TRAU16_FT_FR:
|
||||
ptr_ft = idle_tf_fr;
|
||||
dummy_fill_pl = osmo_gsm611_silence_frame;
|
||||
dummy_fill_pl_len = GSM_FR_BYTES;
|
||||
break;
|
||||
case OSMO_TRAU16_FT_EFR:
|
||||
ptr_ft = idle_tf_efr;
|
||||
dummy_fill_pl = osmo_gsm660_homing_frame;
|
||||
dummy_fill_pl_len = GSM_EFR_BYTES;
|
||||
break;
|
||||
case OSMO_TRAU16_FT_HR:
|
||||
case OSMO_TRAU8_SPEECH:
|
||||
dummy_fill_pl = osmo_gsm620_silence_frame;
|
||||
dummy_fill_pl_len = GSM_HR_BYTES;
|
||||
break;
|
||||
default:
|
||||
/* FIXME: What about 8k subslots and AMR frames? */
|
||||
ptr_ft = idle_tf_spch;
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR, "E1-I.460-IDLE-TX: unsupported frame type\n");
|
||||
goto skip;
|
||||
}
|
||||
|
||||
/* Put the replacement into a message buffer and enqueue it into the
|
||||
* I.460 multiplexer */
|
||||
ptr = msgb_put(msg, E1_TRAU_BITS);
|
||||
memcpy(ptr, ptr_ft, E1_TRAU_BITS);
|
||||
/* turn it into a TRAU-DL frame */
|
||||
memset(&tf, 0, sizeof(tf));
|
||||
tf.dir = OSMO_TRAU_DIR_DL;
|
||||
rc = osmo_rtp2trau(&tf, dummy_fill_pl, dummy_fill_pl_len, endp->e1.trau_rtp_st);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR,
|
||||
"E1-I.460-IDLE-TX: error converting dummy fill frame!\n");
|
||||
goto skip;
|
||||
}
|
||||
rc = osmo_trau_frame_encode(msgb_data(msg), msg->data_len, &tf);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DE1, LOGL_ERROR,
|
||||
"E1-I.460-IDLE-TX: error encoding dummy fill frame!\n");
|
||||
goto skip;
|
||||
}
|
||||
msgb_put(msg, rc);
|
||||
|
||||
/* enqueue it into the I.460 multiplexer */
|
||||
LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-IDLE-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg),
|
||||
osmo_ubit_dump(msgb_data(msg), msgb_length(msg) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg)));
|
||||
osmo_i460_mux_enqueue(endp->e1.schan, msg);
|
||||
return;
|
||||
|
||||
skip:
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_TX_FAIL_CTR));
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
/* called by I.460 de-multiplexer; feed output of I.460 demux into TRAU frame sync */
|
||||
@@ -280,6 +178,7 @@ static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int
|
||||
/* Convert decoded trau frame to RTP frame */
|
||||
struct osmo_trau2rtp_state t2rs = {
|
||||
.type = fr.type,
|
||||
.rtp_extensions = endp->e1.rtp_extensions,
|
||||
};
|
||||
rc = osmo_trau2rtp(msgb_data(msg) + rtp_hdr_len, msg->data_len - rtp_hdr_len, &fr, &t2rs);
|
||||
if (rc <= 0) {
|
||||
@@ -647,18 +546,20 @@ int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8
|
||||
|
||||
/*! Update E1 related parameters (codec and sync pattern).
|
||||
* \param[in] endp endpoint to update. */
|
||||
void mgcp_e1_endp_update(struct mgcp_endpoint *endp)
|
||||
int mgcp_e1_endp_update(struct mgcp_endpoint *endp)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
struct mgcp_conn_rtp *conn_rtp;
|
||||
struct mgcp_rtp_codec *codec;
|
||||
enum osmo_tray_sync_pat_id sync_pat_id;
|
||||
|
||||
/* In order to determine the codec, find the oldest connection on
|
||||
* the endpoint and use its codec information. Normally on an E1
|
||||
* endpoint no more than one connection should exist. */
|
||||
conn = mgcp_conn_get_oldest(endp);
|
||||
conn = mgcp_endp_get_conn_oldest(endp);
|
||||
OSMO_ASSERT(conn);
|
||||
codec = conn->u.rtp.end.codec;
|
||||
conn_rtp = mgcp_conn_get_conn_rtp(conn);
|
||||
codec = conn_rtp->end.cset.codec;
|
||||
OSMO_ASSERT(codec);
|
||||
|
||||
/* Update codec information */
|
||||
@@ -666,9 +567,17 @@ void mgcp_e1_endp_update(struct mgcp_endpoint *endp)
|
||||
determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
|
||||
endp->e1.last_codec = codec;
|
||||
|
||||
/* possible RTP extensions, codec-associated */
|
||||
endp->e1.rtp_extensions = 0;
|
||||
if (codec->param_present && codec->param.fr_efr_twts001)
|
||||
endp->e1.rtp_extensions |= OSMO_RTP_EXT_TWTS001;
|
||||
if (codec->param_present && codec->param.hr_twts002)
|
||||
endp->e1.rtp_extensions |= OSMO_RTP_EXT_TWTS002;
|
||||
|
||||
/* Update sync pattern */
|
||||
sync_pat_id = determine_trau_sync_pat(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp);
|
||||
osmo_trau_sync_set_pat(endp->e1.trau_sync_fi, sync_pat_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Remove E1 resources from endpoint
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/mgcp_protocol.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_iuup.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
|
||||
@@ -58,7 +59,7 @@ static char *gen_virtual_epname(void *ctx, const char *domain,
|
||||
}
|
||||
|
||||
/* Generate E1 endpoint name from given numeric parameters */
|
||||
static char *gen_e1_epname(void *ctx, const char *domain, unsigned int trunk_nr,
|
||||
static char *gen_e1_epname(const void *ctx, const char *domain, unsigned int trunk_nr,
|
||||
uint8_t ts_nr, uint8_t ss_nr)
|
||||
{
|
||||
unsigned int rate;
|
||||
@@ -446,7 +447,7 @@ static uint8_t e1_ss_nr_from_epname(const char *epname)
|
||||
/* Check if the selected E1 endpoint is avalable, which means that none of
|
||||
* the overlapping endpoints are currently serving a call. (if the system
|
||||
* is properly configured such a situation should never ocurr!) */
|
||||
static bool endp_avail_e1(struct mgcp_endpoint *endp)
|
||||
static bool endp_avail_e1(const struct mgcp_endpoint *endp)
|
||||
{
|
||||
/* The following map shows the overlapping of the subslots and their
|
||||
* respective rates. The numbers on the right running from top to bottom
|
||||
@@ -552,7 +553,7 @@ static bool endp_avail_e1(struct mgcp_endpoint *endp)
|
||||
/*! check if an endpoint is available for any kind of operation.
|
||||
* \param[in] endp endpoint to check.
|
||||
* \returns true if endpoint is avalable, false it is blocked for any reason. */
|
||||
bool mgcp_endp_avail(struct mgcp_endpoint *endp)
|
||||
bool mgcp_endp_avail(const struct mgcp_endpoint *endp)
|
||||
{
|
||||
switch (endp->trunk->trunk_type) {
|
||||
case MGCP_TRUNK_VIRTUAL:
|
||||
@@ -569,6 +570,24 @@ bool mgcp_endp_avail(struct mgcp_endpoint *endp)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*! Get number of conns in an endpoint.
|
||||
* \param[in] endp endpoint to check.
|
||||
* \returns Number of connections present in the endpoint. */
|
||||
unsigned int mgcp_endp_num_conns(const struct mgcp_endpoint *endp)
|
||||
{
|
||||
return llist_count(&endp->conns);
|
||||
}
|
||||
|
||||
/*! check if an endpoint can in current state allocate new conns.
|
||||
* \param[in] endp endpoint to check.
|
||||
* \returns true if more connections can be allowed on endpoint, false if it is already busy. */
|
||||
bool mgcp_endp_is_full(const struct mgcp_endpoint *endp)
|
||||
{
|
||||
if (endp->type->max_conns == 0)
|
||||
return false;
|
||||
return mgcp_endp_num_conns(endp) >= endp->type->max_conns;
|
||||
}
|
||||
|
||||
/*! claim endpoint, sets callid and activates endpoint, should be called at the
|
||||
* beginning of the CRCX procedure when it is clear that a new call should be
|
||||
* created.
|
||||
@@ -622,20 +641,85 @@ int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid)
|
||||
|
||||
/*! update endpoint, updates internal endpoint specific data, should be
|
||||
* after when MDCX or CRCX has been executed successuflly.
|
||||
* \param[in] endp endpoint to update. */
|
||||
void mgcp_endp_update(struct mgcp_endpoint *endp)
|
||||
* \param[in] endp endpoint to update.
|
||||
* \returns zero on success, mgcp negative error on failure. */
|
||||
static int mgcp_endp_update_virtual(struct mgcp_endpoint *endp, struct mgcp_conn *conn, enum mgcp_verb verb)
|
||||
{
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->type == MGCP_CONN_TYPE_RTP);
|
||||
struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
|
||||
|
||||
switch (conn_rtp->type) {
|
||||
case MGCP_RTP_DEFAULT:
|
||||
break;
|
||||
case MGCP_RTP_OSMUX:
|
||||
if (conn_osmux_event_rx_crcx_mdcx(conn_rtp) < 0) {
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR, "CRCX: Osmux handling failed!\n");
|
||||
return -500;
|
||||
}
|
||||
break;
|
||||
case MGCP_RTP_IUUP:
|
||||
return mgcp_conn_iuup_event_rx_crcx_mdcx(conn_rtp);
|
||||
default:
|
||||
return -523;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! update endpoint, updates internal endpoint specific data, should be
|
||||
* after when MDCX or CRCX has been executed successuflly.
|
||||
* \param[in] endp endpoint to update.
|
||||
* \returns zero on success, mgcp negative error on failure. */
|
||||
int mgcp_endp_update(struct mgcp_endpoint *endp, struct mgcp_conn *conn, enum mgcp_verb verb)
|
||||
{
|
||||
OSMO_ASSERT(conn);
|
||||
struct mgcp_trunk *trunk = endp->trunk;
|
||||
struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
|
||||
char new_local_addr[INET6_ADDRSTRLEN];
|
||||
|
||||
/* CRCX: Find a local address for conn based on policy and initial SDP remote
|
||||
* information, then find a free port for it.
|
||||
* MDCX: msg may have provided a new remote address, which means we may need
|
||||
* to update our announced IP addr and re-bind our local end. This can
|
||||
* happen for instance if MGW initially provided an IPv4 during CRCX
|
||||
* ACK, and now MDCX tells us the remote has an IPv6 address.
|
||||
*/
|
||||
if (mgcp_get_local_addr(new_local_addr, conn_rtp) < 0)
|
||||
goto fail_bind_port_ret;
|
||||
|
||||
if (strcmp(new_local_addr, conn_rtp->end.local_addr)) {
|
||||
osmo_strlcpy(conn_rtp->end.local_addr, new_local_addr, sizeof(conn_rtp->end.local_addr));
|
||||
mgcp_rtp_end_free_port(&conn_rtp->end);
|
||||
if (mgcp_trunk_allocate_conn_rtp_ports(trunk, conn_rtp) != 0)
|
||||
goto fail_bind_port_ret;
|
||||
}
|
||||
|
||||
/* Allocate resources */
|
||||
switch (endp->trunk->trunk_type) {
|
||||
case MGCP_TRUNK_VIRTUAL:
|
||||
/* No updating initaliziation required for virtual endpoints. */
|
||||
break;
|
||||
return mgcp_endp_update_virtual(endp, conn, verb);
|
||||
case MGCP_TRUNK_E1:
|
||||
mgcp_e1_endp_update(endp);
|
||||
break;
|
||||
return mgcp_e1_endp_update(endp);
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail_bind_port_ret:
|
||||
switch (verb) {
|
||||
case MGCP_VERB_CRCX:
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(trunk->ratectr.mgcp_crcx_ctr_group,
|
||||
MGCP_CRCX_FAIL_BIND_PORT));
|
||||
break;
|
||||
case MGCP_VERB_MDCX:
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(trunk->ratectr.mgcp_mdcx_ctr_group,
|
||||
MGCP_MDCX_FAIL_BIND_PORT));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -500;
|
||||
}
|
||||
|
||||
void mgcp_endp_add_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
|
||||
@@ -655,6 +739,113 @@ void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
|
||||
mgcp_endp_release(endp);
|
||||
}
|
||||
|
||||
/*! free oldest connection in the list.
|
||||
* \param[in] endp associated endpoint */
|
||||
void mgcp_endp_free_conn_oldest(struct mgcp_endpoint *endp)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
if (llist_empty(&endp->conns))
|
||||
return;
|
||||
|
||||
conn = llist_last_entry(&endp->conns, struct mgcp_conn, entry);
|
||||
mgcp_conn_free(conn);
|
||||
}
|
||||
|
||||
/*! free all connections at once.
|
||||
* \param[in] endp associated endpoint */
|
||||
#if defined(__has_attribute)
|
||||
#if __has_attribute(no_sanitize)
|
||||
__attribute__((no_sanitize("undefined"))) /* ubsan detects a misaligned load */
|
||||
#endif
|
||||
#endif
|
||||
void mgcp_endp_free_conn_all(struct mgcp_endpoint *endp)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
/* Drop all items in the list, might be consecutive! */
|
||||
while ((conn = llist_first_entry_or_null(&endp->conns, struct mgcp_conn, entry)))
|
||||
mgcp_conn_free(conn);
|
||||
}
|
||||
|
||||
/*! find a connection by its ID.
|
||||
* \param[in] endp associated endpoint
|
||||
* \param[in] id identification number of the connection
|
||||
* \returns pointer to allocated connection, NULL if not found */
|
||||
struct mgcp_conn *mgcp_endp_get_conn(struct mgcp_endpoint *endp, const char *id)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
const char *id_upper;
|
||||
const char *conn_id;
|
||||
|
||||
if (!id || !*id)
|
||||
return NULL;
|
||||
|
||||
/* Ignore leading zeros in needle */
|
||||
while (*id == '0')
|
||||
id++;
|
||||
|
||||
/* Use uppercase to compare identifiers, to avoid mismatches: RFC3435 2.1.3.2 "Names of
|
||||
* Connections" defines the id as a hex string, so clients may return lower case hex even though
|
||||
* we sent upper case hex in the CRCX response. */
|
||||
id_upper = osmo_str_toupper(id);
|
||||
|
||||
llist_for_each_entry(conn, &endp->conns, entry) {
|
||||
/* Ignore leading zeros in haystack */
|
||||
for (conn_id = conn->id; *conn_id == '0'; conn_id++);
|
||||
|
||||
if (strcmp(conn_id, id_upper) == 0)
|
||||
return conn;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! get oldest connection in the list.
|
||||
* \param[in] endp associated endpoint */
|
||||
struct mgcp_conn *mgcp_endp_get_conn_oldest(struct mgcp_endpoint *endp)
|
||||
{
|
||||
if (llist_empty(&endp->conns))
|
||||
return NULL;
|
||||
|
||||
return llist_last_entry(&endp->conns, struct mgcp_conn, entry);
|
||||
}
|
||||
|
||||
/*! find an RTP connection by its ID.
|
||||
* \param[in] endp associated endpoint
|
||||
* \param[in] id identification number of the connection
|
||||
* \returns pointer to allocated connection, NULL if not found */
|
||||
struct mgcp_conn_rtp *mgcp_endp_get_conn_rtp(struct mgcp_endpoint *endp,
|
||||
const char *id)
|
||||
{
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
conn = mgcp_endp_get_conn(endp, id);
|
||||
if (!conn)
|
||||
return NULL;
|
||||
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP)
|
||||
return mgcp_conn_get_conn_rtp(conn);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Helps assigning a new lco structure, since "codec" is talloc allocated. */
|
||||
void mgcp_endp_update_lco(struct mgcp_endpoint *endp, const struct mgcp_lco *lco)
|
||||
{
|
||||
/* First free old talloc allocated codec string: */
|
||||
talloc_free(endp->local_options.codec);
|
||||
endp->local_options.codec = NULL;
|
||||
|
||||
if (lco) {
|
||||
endp->local_options = *lco;
|
||||
if (lco->codec)
|
||||
endp->local_options.codec = talloc_strdup(endp, lco->codec);
|
||||
} else {
|
||||
endp->local_options = (struct mgcp_lco){0};
|
||||
}
|
||||
}
|
||||
|
||||
/*! release endpoint, all open connections are closed.
|
||||
* \param[in] endp endpoint to release */
|
||||
void mgcp_endp_release(struct mgcp_endpoint *endp)
|
||||
@@ -665,7 +856,7 @@ void mgcp_endp_release(struct mgcp_endpoint *endp)
|
||||
* all connections have been removed already. In case
|
||||
* that there are still connections open (e.g. when
|
||||
* RSIP is executed), free them all at once. */
|
||||
mgcp_conn_free_all(endp);
|
||||
mgcp_endp_free_conn_all(endp);
|
||||
|
||||
/* We must only decrement the stat item when the endpoint as actually
|
||||
* claimed. An endpoint is claimed when a call-id is set */
|
||||
@@ -676,10 +867,7 @@ void mgcp_endp_release(struct mgcp_endpoint *endp)
|
||||
/* Reset endpoint parameters and states */
|
||||
talloc_free(endp->callid);
|
||||
endp->callid = NULL;
|
||||
talloc_free(endp->local_options.string);
|
||||
endp->local_options.string = NULL;
|
||||
talloc_free(endp->local_options.codec);
|
||||
endp->local_options.codec = NULL;
|
||||
mgcp_endp_update_lco(endp, NULL);
|
||||
|
||||
if (endp->trunk->trunk_type == MGCP_TRUNK_E1) {
|
||||
uint8_t ts = e1_ts_nr_from_epname(endp->name);
|
||||
|
||||
@@ -264,6 +264,7 @@ static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mg
|
||||
uint8_t *amr_data;
|
||||
struct rtp_hdr *rtp_hdr;
|
||||
struct amr_hdr *amr_hdr;
|
||||
struct mgcp_rtp_codec *dst_codec;
|
||||
int rc;
|
||||
|
||||
ft = osmo_amr_bytes_to_ft(msgb_l3len(msg));
|
||||
@@ -275,7 +276,8 @@ static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mg
|
||||
}
|
||||
msgb_pull_to_l3(msg);
|
||||
|
||||
if (mgcp_codec_amr_is_octet_aligned(conn_rtp_dst->end.codec)) {
|
||||
dst_codec = conn_rtp_dst->end.cset.codec;
|
||||
if (mgcp_codec_amr_is_octet_aligned(dst_codec)) {
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR OA: ft %d, len %d\n", ft, msgb_length(msg));
|
||||
amr_hdr = (struct amr_hdr *) msgb_push(msg, sizeof(struct amr_hdr));
|
||||
amr_hdr->cmr = 15; /* no change */
|
||||
@@ -303,7 +305,7 @@ static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mg
|
||||
.extension = 0,
|
||||
.padding = 0,
|
||||
.version = 0,
|
||||
.payload_type = conn_rtp_dst->end.codec->payload_type,
|
||||
.payload_type = dst_codec->payload_type,
|
||||
.marker = 0,
|
||||
.sequence = frame_nr,
|
||||
.timestamp = 0,
|
||||
@@ -502,7 +504,7 @@ static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
|
||||
* ignored by the receiver, but still it's useful for debug purposes
|
||||
* to set it. Moreover, it seems ip.access nano3g produces much worse
|
||||
* audio output on the air side if timestamp is not set properly. */
|
||||
hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->codec->rate));
|
||||
hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->cset.codec->rate));
|
||||
hdr->sequence = osmo_htons(rtp_state->alt_rtp_tx_sequence);
|
||||
hdr->ssrc = rtp_state->alt_rtp_tx_ssrc;
|
||||
rtp_state->alt_rtp_tx_sequence++;
|
||||
@@ -544,13 +546,13 @@ static int _conn_iuup_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
|
||||
|
||||
msgb_pull_to_l2(msg);
|
||||
rtph = (struct rtp_hdr *)msgb_push(msg, sizeof(*rtph));
|
||||
/* TODO: fill rtph properly: */
|
||||
/* rtph is further filled in mgcp_send_iuup() below. */
|
||||
*rtph = (struct rtp_hdr){
|
||||
.csrc_count = 0,
|
||||
.extension = 0,
|
||||
.padding = 0,
|
||||
.version = 2,
|
||||
.payload_type = conn_rtp_dst->end.codec->payload_type,
|
||||
.payload_type = conn_rtp_dst->end.cset.codec->payload_type,
|
||||
.marker = 0,
|
||||
.sequence = 0,
|
||||
.timestamp = 0,
|
||||
@@ -645,6 +647,7 @@ int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn
|
||||
struct rtp_hdr *rtph;
|
||||
int rc = -1;
|
||||
int iuup_length = 0;
|
||||
struct mgcp_rtp_codec *src_codec;
|
||||
int8_t rfci;
|
||||
|
||||
/* Tx RNL-DATA.req */
|
||||
@@ -657,13 +660,14 @@ int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn
|
||||
|
||||
/* TODO: CMR handling & multiple frames handling */
|
||||
|
||||
if (strcmp(conn_src_rtp->end.codec->subtype_name, "AMR") != 0) {
|
||||
src_codec = conn_src_rtp->end.cset.codec;
|
||||
if (strcmp(src_codec->subtype_name, "AMR") != 0) {
|
||||
LOG_CONN_RTP(conn_src_rtp, LOGL_ERROR,
|
||||
"Bridge RTP=>IuUP: Bridging src codec %s to IuUP AMR not supported\n",
|
||||
conn_src_rtp->end.codec->subtype_name);
|
||||
src_codec->subtype_name);
|
||||
goto free_ret;
|
||||
}
|
||||
if (mgcp_codec_amr_is_octet_aligned(conn_src_rtp->end.codec)) {
|
||||
if (mgcp_codec_amr_is_octet_aligned(src_codec)) {
|
||||
struct amr_hdr *amr_hdr = (struct amr_hdr *) msgb_data(msg);
|
||||
if (msgb_length(msg) < (sizeof(*amr_hdr))) {
|
||||
LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE,
|
||||
@@ -749,3 +753,44 @@ int mgcp_conn_iuup_send_dummy(struct mgcp_conn_rtp *conn_rtp)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* To be called every time an CRCX/MDCX is received.
|
||||
* returns: 0 if conn can continue, MGCP negative code if an error ocurred during setup */
|
||||
int mgcp_conn_iuup_event_rx_crcx_mdcx(struct mgcp_conn_rtp *conn_rtp)
|
||||
{
|
||||
struct mgcp_conn *peer_conn;
|
||||
struct mgcp_conn_rtp *peer_conn_rtp;
|
||||
OSMO_ASSERT(mgcp_conn_rtp_is_iuup(conn_rtp));
|
||||
|
||||
/* keep waiting to receive remote address through CRCX/MDCX */
|
||||
if (!mgcp_rtp_end_remote_addr_available(&conn_rtp->end))
|
||||
return 0;
|
||||
|
||||
/* Conn already IuUP-configured/initialized, nothing to be done. */
|
||||
if (conn_rtp->iuup.configured)
|
||||
return 0;
|
||||
|
||||
/* Reached this point, conn_rtp is an IuUP conn which can be configured
|
||||
* and was not yet configured. If its sister conn in the endpoint was
|
||||
* already configured as IuUP "passive", then we know this one can be
|
||||
* configured as IuUP "active" right now. In that case, do so.
|
||||
* This usually happens on an HNBGW co-located MGW, where during
|
||||
* RAB-ASS-REQ the RAN-side conn (sister conn here) was already
|
||||
* configured and then upon rx of RAB-ASS-RESP it sets up (CRCX) the
|
||||
* CN-side IuUP conn (rt_conn here).
|
||||
*/
|
||||
peer_conn = mgcp_find_dst_conn(conn_rtp->conn);
|
||||
/* No peer conn yet, nothing to be done. */
|
||||
if (!peer_conn)
|
||||
return 0;
|
||||
peer_conn_rtp = mgcp_conn_get_conn_rtp(peer_conn);
|
||||
|
||||
if (mgcp_conn_rtp_is_iuup(peer_conn_rtp) &&
|
||||
peer_conn_rtp->iuup.configured &&
|
||||
!conn_rtp->iuup.active_init) {
|
||||
LOG_CONN_RTP(conn_rtp, LOGL_INFO, "Sister IuUP conn in endp configured as passive, init this one as active\n");
|
||||
OSMO_ASSERT(peer_conn_rtp->iuup.init_ind);
|
||||
_conn_iuup_configure_as_active(conn_rtp, peer_conn_rtp->iuup.init_ind);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/mgcp/osmux.h>
|
||||
@@ -33,6 +34,10 @@
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
|
||||
/* (same fmt as LOGPENDP()) */
|
||||
#define LOG_MGCP_PDATA(PDATA, LEVEL, FMT, ARGS...) \
|
||||
LOGP(DLMGCP, LEVEL, "%s: endpoint(%s) " FMT, (PDATA)->rq->name, (PDATA)->epname ? : "null-epname", ##ARGS)
|
||||
|
||||
/*! Display an mgcp message on the log output.
|
||||
* \param[in] message mgcp message string
|
||||
* \param[in] len message mgcp message string length
|
||||
@@ -76,61 +81,215 @@ void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble)
|
||||
|
||||
/*! Parse connection mode.
|
||||
* \param[in] mode as string (recvonly, sendrecv, sendonly confecho or loopback)
|
||||
* \param[in] endp pointer to endpoint (only used for log output)
|
||||
* \param[out] associated connection to be modified accordingly
|
||||
* \returns 0 on success, -1 on error */
|
||||
int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn *conn)
|
||||
* \returns MGCP_CONN_* on success, MGCP_CONN_NONE on error */
|
||||
enum mgcp_connection_mode mgcp_parse_conn_mode(const char *mode)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!mode) {
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
|
||||
"missing connection mode\n");
|
||||
return -1;
|
||||
}
|
||||
if (!conn)
|
||||
return -1;
|
||||
if (!endp)
|
||||
return -1;
|
||||
if (!mode)
|
||||
return MGCP_CONN_NONE;
|
||||
|
||||
if (strcasecmp(mode, "recvonly") == 0)
|
||||
conn->mode = MGCP_CONN_RECV_ONLY;
|
||||
else if (strcasecmp(mode, "sendrecv") == 0)
|
||||
conn->mode = MGCP_CONN_RECV_SEND;
|
||||
else if (strcasecmp(mode, "sendonly") == 0)
|
||||
conn->mode = MGCP_CONN_SEND_ONLY;
|
||||
else if (strcasecmp(mode, "confecho") == 0)
|
||||
conn->mode = MGCP_CONN_CONFECHO;
|
||||
else if (strcasecmp(mode, "loopback") == 0)
|
||||
conn->mode = MGCP_CONN_LOOPBACK;
|
||||
else {
|
||||
LOGPCONN(conn, DLMGCP, LOGL_ERROR,
|
||||
"unknown connection mode: '%s'\n", mode);
|
||||
ret = -1;
|
||||
return MGCP_CONN_RECV_ONLY;
|
||||
if (strcasecmp(mode, "sendrecv") == 0)
|
||||
return MGCP_CONN_RECV_SEND;
|
||||
if (strcasecmp(mode, "sendonly") == 0)
|
||||
return MGCP_CONN_SEND_ONLY;
|
||||
if (strcasecmp(mode, "confecho") == 0)
|
||||
return MGCP_CONN_CONFECHO;
|
||||
if (strcasecmp(mode, "loopback") == 0)
|
||||
return MGCP_CONN_LOOPBACK;
|
||||
return MGCP_CONN_NONE;
|
||||
}
|
||||
|
||||
/*! 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++;
|
||||
}
|
||||
|
||||
/* Special handling for RTP connections */
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
conn->u.rtp.end.output_enabled = !!(conn->mode & MGCP_CONN_SEND_ONLY);
|
||||
/* Check if we got any result */
|
||||
if (*ptr == ':')
|
||||
return NULL;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/*! Check the LCO option. This function checks for multiple appearance 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 (strcasecmp(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;
|
||||
}
|
||||
|
||||
/* Set the LCO from a string (see RFC 3435).
|
||||
* The string is stored in the 'string' field. A NULL string is handled exactly
|
||||
* like an empty string, the 'string' field is never NULL after this function
|
||||
* has been called. */
|
||||
static int mgcp_parse_lco(void *ctx, struct mgcp_lco *lco, const char *options)
|
||||
{
|
||||
const char *lco_id;
|
||||
char codec[17];
|
||||
char nt[17];
|
||||
int len;
|
||||
|
||||
if (!options)
|
||||
return 0;
|
||||
if (strlen(options) == 0)
|
||||
return 0;
|
||||
|
||||
lco->present = true;
|
||||
|
||||
/* Make sure the encoding of the LCO is consistent 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;
|
||||
}
|
||||
|
||||
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn));
|
||||
lco_id = options;
|
||||
while ((lco_id = get_lco_identifier(lco_id))) {
|
||||
switch (tolower(lco_id[0])) {
|
||||
case 'p':
|
||||
if (sscanf(lco_id + 1, ":%d-%d",
|
||||
&lco->pkt_period_min, &lco->pkt_period_max) == 1)
|
||||
lco->pkt_period_max = lco->pkt_period_min;
|
||||
break;
|
||||
case 'a':
|
||||
/* FIXME: LCO also supports the negotiation of more than one codec.
|
||||
* (e.g. a:PCMU;G726-32) But this implementation only supports a single
|
||||
* codec only. Ignoring all but the first codec. */
|
||||
if (sscanf(lco_id + 1, ":%16[^,;]", codec) == 1) {
|
||||
talloc_free(lco->codec);
|
||||
/* MGCP header is case insensive, and we'll need
|
||||
codec in uppercase when using it later: */
|
||||
len = strlen(codec);
|
||||
lco->codec = talloc_size(ctx, len + 1);
|
||||
osmo_str_toupper_buf(lco->codec, len + 1, codec);
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
if (lco_id[1] == 't' && sscanf(lco_id + 2, ":%16[^,]", nt) == 1)
|
||||
break;
|
||||
/* else: fall through to print notice log */
|
||||
default:
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"LCO: unhandled option: '%c'/%d in \"%s\"\n",
|
||||
*lco_id, *lco_id, options);
|
||||
break;
|
||||
}
|
||||
|
||||
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "connection mode '%s' %d\n",
|
||||
mode, conn->mode);
|
||||
|
||||
/* Special handling für RTP connections */
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %u\n",
|
||||
conn->u.rtp.end.output_enabled);
|
||||
lco_id = strchr(lco_id, ',');
|
||||
if (!lco_id)
|
||||
break;
|
||||
}
|
||||
|
||||
/* The VTY might change the connection mode at any time, so we have
|
||||
* to hold a copy of the original connection mode */
|
||||
conn->mode_orig = conn->mode;
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"local CX options: lco->pkt_period_max: %d, lco->codec: %s\n",
|
||||
lco->pkt_period_max, lco->codec);
|
||||
|
||||
return ret;
|
||||
/* Check if the packetization fits the 20ms raster */
|
||||
if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"local CX options: packetization interval is not a multiple of 20ms!\n");
|
||||
return -535;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Analyze and parse the the hader of an MGCP messeage string.
|
||||
@@ -159,8 +318,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
|
||||
break;
|
||||
case 2:
|
||||
if (strcasecmp("MGCP", elem)) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"MGCP header parsing error\n");
|
||||
LOG_MGCP_PDATA(pdata, LOGL_ERROR, "MGCP header parsing error\n");
|
||||
return -510;
|
||||
}
|
||||
break;
|
||||
@@ -173,13 +331,121 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
|
||||
}
|
||||
|
||||
if (i != 4) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "MGCP status line too short.\n");
|
||||
LOG_MGCP_PDATA(pdata, LOGL_ERROR, "MGCP status line too short.\n");
|
||||
return -510;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool parse_x_osmo_ign(struct mgcp_parse_data *pdata, char *line)
|
||||
{
|
||||
char *saveptr = NULL;
|
||||
|
||||
if (strncasecmp(line, MGCP_X_OSMO_IGN_HEADER, strlen(MGCP_X_OSMO_IGN_HEADER)))
|
||||
return false;
|
||||
line += strlen(MGCP_X_OSMO_IGN_HEADER);
|
||||
|
||||
while (1) {
|
||||
char *token = strtok_r(line, " ", &saveptr);
|
||||
line = NULL;
|
||||
if (!token)
|
||||
break;
|
||||
|
||||
if (!strcasecmp(token, "C"))
|
||||
pdata->hpars.x_osmo_ign |= MGCP_X_OSMO_IGN_CALLID;
|
||||
else
|
||||
LOG_MGCP_PDATA(pdata, LOGL_ERROR, "received unknown X-Osmo-IGN item '%s'\n", token);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*! Analyze and parse the the header of an MGCP message string.
|
||||
* \param[inout] pdata caller provided memory to store the parsing results.
|
||||
* \returns 0 when parsing was successful, negative (MGCP cause code) on error. */
|
||||
int mgcp_parse_hdr_pars(struct mgcp_parse_data *pdata)
|
||||
{
|
||||
struct mgcp_parse_hdr_pars *hp = &pdata->hpars;
|
||||
char *line;
|
||||
int rc;
|
||||
|
||||
mgcp_parse_hdr_pars_init(hp);
|
||||
|
||||
for_each_line(line, pdata->save) {
|
||||
if (!mgcp_check_param(line)) {
|
||||
LOG_MGCP_PDATA(pdata, LOGL_NOTICE, "wrong MGCP option format: '%s'\n", line);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (toupper(line[0])) {
|
||||
case 'L':
|
||||
hp->lco_string = (const char *)line + 3;
|
||||
rc = mgcp_parse_lco(pdata, &hp->lco, hp->lco_string);
|
||||
if (rc < 0) {
|
||||
LOG_MGCP_PDATA(pdata, LOGL_NOTICE, "Invalid LocalConnectionOptions line: '%s'", line);
|
||||
switch (pdata->rq->verb) {
|
||||
case MGCP_VERB_CRCX:
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(pdata->rq->trunk->ratectr.mgcp_crcx_ctr_group,
|
||||
MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
|
||||
break;
|
||||
case MGCP_VERB_MDCX:
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(pdata->rq->trunk->ratectr.mgcp_mdcx_ctr_group,
|
||||
MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
break;
|
||||
case 'C':
|
||||
hp->callid = (const char *)line + 3;
|
||||
break;
|
||||
case 'I':
|
||||
hp->connid = (const char *)line + 3;
|
||||
break;
|
||||
case 'M':
|
||||
hp->mode = mgcp_parse_conn_mode((const char *)line + 3);
|
||||
break;
|
||||
case 'X':
|
||||
if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
|
||||
hp->remote_osmux_cid = mgcp_parse_osmux_cid(line);
|
||||
break;
|
||||
}
|
||||
if (parse_x_osmo_ign(pdata, line))
|
||||
break;
|
||||
/* Ignore unknown X-headers */
|
||||
break;
|
||||
case '\0':
|
||||
hp->have_sdp = true;
|
||||
goto mgcp_header_done;
|
||||
default:
|
||||
LOG_MGCP_PDATA(pdata, LOGL_NOTICE, "unhandled option: '%c'/%d\n", *line, *line);
|
||||
switch (pdata->rq->verb) {
|
||||
case MGCP_VERB_CRCX:
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(pdata->rq->trunk->ratectr.mgcp_crcx_ctr_group,
|
||||
MGCP_CRCX_FAIL_UNHANDLED_PARAM));
|
||||
break;
|
||||
case MGCP_VERB_MDCX:
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(pdata->rq->trunk->ratectr.mgcp_mdcx_ctr_group,
|
||||
MGCP_MDCX_FAIL_UNHANDLED_PARAM));
|
||||
break;
|
||||
case MGCP_VERB_DLCX:
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(pdata->rq->trunk->ratectr.mgcp_dlcx_ctr_group,
|
||||
MGCP_DLCX_FAIL_UNHANDLED_PARAM));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -539;
|
||||
}
|
||||
}
|
||||
|
||||
mgcp_header_done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Extract OSMUX CID from an MGCP parameter line (string).
|
||||
* \param[in] line single parameter line from the MGCP message
|
||||
* \returns OSMUX CID, -1 wildcard, -2 on error */
|
||||
@@ -190,19 +456,19 @@ int mgcp_parse_osmux_cid(const char *line)
|
||||
|
||||
if (strcasecmp(line + 2, "Osmux: *") == 0) {
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Parsed wilcard Osmux CID\n");
|
||||
return -1;
|
||||
return MGCP_PARSE_HDR_PARS_OSMUX_CID_WILDCARD;
|
||||
}
|
||||
|
||||
if (sscanf(line + 2 + 7, "%u", &osmux_cid) != 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed parsing Osmux in MGCP msg line: %s\n",
|
||||
line);
|
||||
return -2;
|
||||
return MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET;
|
||||
}
|
||||
|
||||
if (osmux_cid > OSMUX_CID_MAX) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
|
||||
osmux_cid, OSMUX_CID_MAX);
|
||||
return -2;
|
||||
return MGCP_PARSE_HDR_PARS_OSMUX_CID_UNSET;
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "MGCP client offered Osmux CID %u\n", osmux_cid);
|
||||
|
||||
@@ -210,20 +476,13 @@ int mgcp_parse_osmux_cid(const char *line)
|
||||
}
|
||||
|
||||
/*! Check MGCP parameter line (string) for plausibility.
|
||||
* \param[in] endp pointer to endpoint (only used for log output, may be NULL)
|
||||
* \param[in] trunk pointer to trunk (only used for log output, may be NULL if endp is not NULL)
|
||||
* \param[in] line single parameter line from the MGCP message
|
||||
* \returns true when line seems plausible, false on error */
|
||||
bool mgcp_check_param(const struct mgcp_endpoint *endp, struct mgcp_trunk *trunk, const char *line)
|
||||
bool mgcp_check_param(const char *line)
|
||||
{
|
||||
const size_t line_len = strlen(line);
|
||||
if (line[0] != '\0' && line_len < 2) {
|
||||
if (endp)
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "wrong MGCP option format: '%s'\n", line);
|
||||
else
|
||||
LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE, "wrong MGCP option format: '%s'\n", line);
|
||||
if (line[0] != '\0' && line_len < 2)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* FIXME: A couple more checks wouldn't hurt... */
|
||||
|
||||
@@ -242,10 +501,6 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
|
||||
if (!endp)
|
||||
return -1;
|
||||
|
||||
/* Accept any CallID for "X-Osmo-IGN: C" */
|
||||
if (endp->x_osmo_ign & MGCP_X_OSMO_IGN_CALLID)
|
||||
return 0;
|
||||
|
||||
if (!callid)
|
||||
return -1;
|
||||
if (!endp->callid)
|
||||
@@ -294,7 +549,7 @@ int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
|
||||
}
|
||||
|
||||
/* Check if connection exists */
|
||||
if (mgcp_conn_get(endp, conn_id))
|
||||
if (mgcp_endp_get_conn(endp, conn_id))
|
||||
return 0;
|
||||
|
||||
LOGPENDP(endp, DLMGCP, LOGL_ERROR,
|
||||
|
||||
@@ -85,12 +85,6 @@ static inline struct msgb *mgw_msgb_copy_c(void *ctx, struct msgb *msg, const ch
|
||||
|
||||
static int rx_rtp(struct msgb *msg);
|
||||
|
||||
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end)
|
||||
{
|
||||
return (osmo_sockaddr_port(&rtp_end->addr.u.sa) != 0) &&
|
||||
(osmo_sockaddr_is_any(&rtp_end->addr) == 0);
|
||||
}
|
||||
|
||||
/*! Determine the local rtp bind IP-address.
|
||||
* \param[out] addr caller provided memory to store the resulting IP-Address.
|
||||
* \param[in] endp mgcp endpoint, that holds a copy of the VTY parameters.
|
||||
@@ -338,7 +332,7 @@ static int adjust_rtp_timestamp_offset(const struct mgcp_endpoint *endp,
|
||||
osmo_sockaddr_ntop(&addr->u.sa, ipbuf),
|
||||
osmo_sockaddr_port(&addr->u.sa));
|
||||
} else {
|
||||
tsdelta = rtp_end->codec->rate * 20 / 1000;
|
||||
tsdelta = rtp_end->cset.codec->rate * 20 / 1000;
|
||||
LOGPENDP(endp, DRTP, LOGL_NOTICE,
|
||||
"Fixed packet duration and last timestamp delta "
|
||||
"are not available, "
|
||||
@@ -414,31 +408,6 @@ static int align_rtp_timestamp_offset(const struct mgcp_endpoint *endp,
|
||||
return ts_error;
|
||||
}
|
||||
|
||||
/*! dummy callback to disable transcoding (see also cfg->rtp_processing_cb).
|
||||
* \param[in] associated endpoint.
|
||||
* \param[in] destination RTP end.
|
||||
* \param[in,out] msg message bufffer containing data. Function might change length.
|
||||
* \returns ignores input parameters, return always 0. */
|
||||
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_end *dst_end,
|
||||
struct msgb *msg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! dummy callback to disable transcoding (see also cfg->setup_rtp_processing_cb).
|
||||
* \param[in] associated endpoint.
|
||||
* \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_conn_rtp *conn_dst,
|
||||
struct mgcp_conn_rtp *conn_src)
|
||||
{
|
||||
LOGPENDP(endp, DRTP, LOGL_DEBUG, "transcoding disabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mgcp_rtp_annex_count(const struct mgcp_endpoint *endp,
|
||||
struct mgcp_rtp_state *state, const uint16_t seq,
|
||||
const int32_t transit, const uint32_t ssrc,
|
||||
@@ -502,11 +471,11 @@ static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
|
||||
}
|
||||
rtp_hdr = (struct rtp_hdr *)msgb_data(msg);
|
||||
|
||||
if (!conn_dst->end.codec) {
|
||||
if (!conn_dst->end.cset.codec) {
|
||||
LOG_CONN_RTP(conn_dst, LOGL_NOTICE, "no codec set on destination connection!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
rtp_hdr->payload_type = (uint8_t) conn_dst->end.codec->payload_type;
|
||||
rtp_hdr->payload_type = (uint8_t) conn_dst->end.cset.codec->payload_type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -529,7 +498,8 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
|
||||
uint32_t timestamp, ssrc;
|
||||
bool marker_bit;
|
||||
struct rtp_hdr *rtp_hdr;
|
||||
int payload = rtp_end->codec->payload_type;
|
||||
struct mgcp_rtp_codec *codec = rtp_end->cset.codec;
|
||||
int payload = codec->payload_type;
|
||||
unsigned int len = msgb_length(msg);
|
||||
|
||||
if (len < sizeof(*rtp_hdr))
|
||||
@@ -538,7 +508,7 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
|
||||
rtp_hdr = (struct rtp_hdr *)msgb_data(msg);
|
||||
seq = ntohs(rtp_hdr->sequence);
|
||||
timestamp = ntohl(rtp_hdr->timestamp);
|
||||
arrival_time = mgcp_get_current_ts(rtp_end->codec->rate);
|
||||
arrival_time = mgcp_get_current_ts(codec->rate);
|
||||
ssrc = ntohl(rtp_hdr->ssrc);
|
||||
marker_bit = !!rtp_hdr->marker;
|
||||
transit = arrival_time - timestamp;
|
||||
@@ -547,16 +517,16 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
|
||||
|
||||
if (!state->initialized) {
|
||||
state->initialized = 1;
|
||||
state->packet_duration = mgcp_rtp_packet_duration(endp, rtp_end);
|
||||
state->in_stream.last_seq = seq - 1;
|
||||
state->in_stream.ssrc = state->patch.orig_ssrc = ssrc;
|
||||
state->in_stream.ssrc = ssrc;
|
||||
state->in_stream.last_tsdelta = 0;
|
||||
state->packet_duration =
|
||||
mgcp_rtp_packet_duration(endp, rtp_end);
|
||||
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 */
|
||||
state->patch.orig_ssrc = ssrc;
|
||||
state->patch.patch_ssrc = rtp_end->force_constant_ssrc;
|
||||
LOGPENDP(endp, DRTP, LOGL_INFO,
|
||||
"initializing stream, SSRC: %u timestamp: %u "
|
||||
"pkt-duration: %d, from %s:%d\n",
|
||||
@@ -566,7 +536,7 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
|
||||
osmo_sockaddr_port(&addr->u.sa));
|
||||
if (state->packet_duration == 0) {
|
||||
state->packet_duration =
|
||||
rtp_end->codec->rate * 20 / 1000;
|
||||
codec->rate * 20 / 1000;
|
||||
LOGPENDP(endp, DRTP, LOGL_NOTICE,
|
||||
"fixed packet duration is not available, "
|
||||
"using fixed 20ms instead: %d from %s:%d\n",
|
||||
@@ -583,7 +553,7 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
|
||||
osmo_sockaddr_port(&addr->u.sa));
|
||||
|
||||
state->in_stream.ssrc = ssrc;
|
||||
if (rtp_end->force_constant_ssrc) {
|
||||
if (state->patch.patch_ssrc) {
|
||||
int16_t delta_seq;
|
||||
|
||||
/* Always increment seqno by 1 */
|
||||
@@ -599,10 +569,7 @@ void mgcp_patch_and_count(const struct mgcp_endpoint *endp,
|
||||
adjust_rtp_timestamp_offset(endp, state, rtp_end, addr,
|
||||
delta_seq, timestamp, marker_bit);
|
||||
|
||||
state->patch.patch_ssrc = true;
|
||||
ssrc = state->patch.orig_ssrc;
|
||||
if (rtp_end->force_constant_ssrc != -1)
|
||||
rtp_end->force_constant_ssrc -= 1;
|
||||
|
||||
LOGPENDP(endp, DRTP, LOGL_NOTICE,
|
||||
"SSRC patching enabled, SSRC: %u "
|
||||
@@ -822,8 +789,8 @@ static void gen_rtp_header(struct msgb *msg, struct mgcp_rtp_end *rtp_end,
|
||||
return;
|
||||
|
||||
hdr->version = 2;
|
||||
hdr->payload_type = rtp_end->codec->payload_type;
|
||||
hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->codec->rate));
|
||||
hdr->payload_type = rtp_end->cset.codec->payload_type;
|
||||
hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->cset.codec->rate));
|
||||
hdr->sequence = osmo_htons(state->alt_rtp_tx_sequence);
|
||||
hdr->ssrc = state->alt_rtp_tx_ssrc;
|
||||
}
|
||||
@@ -1189,34 +1156,32 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
|
||||
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port)
|
||||
);
|
||||
} else if (is_rtp) {
|
||||
struct mgcp_rtp_codec *src_codec;
|
||||
struct mgcp_rtp_codec *dst_codec;
|
||||
/* Make sure we have a valid RTP header, in cases where no RTP
|
||||
* header is present, we will generate one. */
|
||||
gen_rtp_header(msg, rtp_end, rtp_state);
|
||||
|
||||
/* Run transcoder */
|
||||
rc = endp->trunk->cfg->rtp_processing_cb(endp, rtp_end, msg);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "Error %d during transcoding\n", rc);
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
/* TODO: Run transcoder */
|
||||
|
||||
if (addr)
|
||||
mgcp_patch_and_count(endp, rtp_state, rtp_end, addr, msg);
|
||||
|
||||
src_codec = conn_src->end.cset.codec;
|
||||
dst_codec = conn_dst->end.cset.codec;
|
||||
if (mgcp_conn_rtp_is_iuup(conn_dst) || mgcp_conn_rtp_is_iuup(conn_src)) {
|
||||
/* the iuup code will correctly transform to the correct AMR mode */
|
||||
} else if (mgcp_codec_amr_align_mode_is_indicated(conn_dst->end.codec)) {
|
||||
rc = amr_oa_bwe_convert(endp, msg, conn_dst->end.codec->param.amr_octet_aligned);
|
||||
} else if (mgcp_codec_amr_align_mode_is_indicated(dst_codec)) {
|
||||
rc = amr_oa_bwe_convert(endp, msg, dst_codec->param.amr_octet_aligned);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"Error in AMR octet-aligned <-> bandwidth-efficient mode conversion (target=%s)\n",
|
||||
conn_dst->end.codec->param.amr_octet_aligned ? "octet-aligned" : "bandwidth-efficient");
|
||||
dst_codec->param.amr_octet_aligned ? "octet-aligned" : "bandwidth-efficient");
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
} else if (rtp_end->rfc5993_hr_convert &&
|
||||
strcmp(conn_src->end.codec->subtype_name, "GSM-HR-08") == 0) {
|
||||
strcmp(src_codec->subtype_name, "GSM-HR-08") == 0) {
|
||||
rc = rfc5993_hr_convert(endp, msg);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "Error while converting to GSM-HR-08\n");
|
||||
@@ -1343,13 +1308,13 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
|
||||
* packets back to their origin. We will use the originating
|
||||
* address data from the UDP packet header to patch the
|
||||
* outgoing address in connection on the fly */
|
||||
if (osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa) == 0) {
|
||||
memcpy(&conn->u.rtp.end.addr, from_addr,
|
||||
sizeof(conn->u.rtp.end.addr));
|
||||
if (osmo_sockaddr_port(&conn_src->end.addr.u.sa) == 0) {
|
||||
memcpy(&conn_src->end.addr, from_addr,
|
||||
sizeof(conn_src->end.addr));
|
||||
LOG_CONN_RTP(conn_src, LOGL_NOTICE,
|
||||
"loopback mode: implicitly using source address (%s:%u) as destination address\n",
|
||||
osmo_sockaddr_ntop(&from_addr->u.sa, ipbuf),
|
||||
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa));
|
||||
osmo_sockaddr_port(&conn_src->end.addr.u.sa));
|
||||
}
|
||||
return mgcp_conn_rtp_dispatch_rtp(conn_src, msg);
|
||||
}
|
||||
@@ -1428,19 +1393,19 @@ int mgcp_dispatch_e1_bridge_cb(struct msgb *msg)
|
||||
* packets back to their origin. We will use the originating
|
||||
* address data from the UDP packet header to patch the
|
||||
* outgoing address in connection on the fly */
|
||||
if (osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa) == 0) {
|
||||
memcpy(&conn->u.rtp.end.addr, from_addr,
|
||||
sizeof(conn->u.rtp.end.addr));
|
||||
if (osmo_sockaddr_port(&conn_src->end.addr.u.sa) == 0) {
|
||||
memcpy(&conn_src->end.addr, from_addr,
|
||||
sizeof(conn_src->end.addr));
|
||||
LOG_CONN_RTP(conn_src, LOGL_NOTICE,
|
||||
"loopback mode: implicitly using source address (%s:%u) as destination address\n",
|
||||
osmo_sockaddr_ntop(&from_addr->u.sa, ipbuf),
|
||||
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa));
|
||||
osmo_sockaddr_port(&conn_src->end.addr.u.sa));
|
||||
}
|
||||
return mgcp_conn_rtp_dispatch_rtp(conn_src, msg);
|
||||
}
|
||||
|
||||
/* Forward to E1 */
|
||||
return mgcp_e1_send_rtp(conn->endp, conn->u.rtp.end.codec, msg);
|
||||
return mgcp_e1_send_rtp(conn->endp, conn_src->end.cset.codec, msg);
|
||||
}
|
||||
|
||||
/*! cleanup an endpoint when a connection on an RTP bridge endpoint is removed.
|
||||
@@ -1562,18 +1527,18 @@ static int rx_rtp(struct msgb *msg)
|
||||
|
||||
/* Handle AMR frame format conversion (octet-aligned vs. bandwith-efficient) */
|
||||
if (mc->proto == MGCP_PROTO_RTP
|
||||
&& conn_src->end.codec
|
||||
&& mgcp_codec_amr_align_mode_is_indicated(conn_src->end.codec)) {
|
||||
&& conn_src->end.cset.codec
|
||||
&& mgcp_codec_amr_align_mode_is_indicated(conn_src->end.cset.codec)) {
|
||||
/* Make sure that the incoming AMR frame format matches the frame format that the call agent has
|
||||
* communicated via SDP when the connection was created/modfied. */
|
||||
int oa = amr_oa_check((char*)msgb_data(msg), msgb_length(msg));
|
||||
if (oa < 0)
|
||||
goto out_free;
|
||||
if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned) {
|
||||
if (((bool)oa) != conn_src->end.cset.codec->param.amr_octet_aligned) {
|
||||
LOG_CONN_RTP(conn_src, LOGL_NOTICE,
|
||||
"rx_rtp(%u bytes): Expected RTP AMR octet-aligned=%u but got octet-aligned=%u."
|
||||
" check the config of your call-agent!\n",
|
||||
msgb_length(msg), conn_src->end.codec->param.amr_octet_aligned, oa);
|
||||
msgb_length(msg), conn_src->end.cset.codec->param.amr_octet_aligned, oa);
|
||||
goto out_free;
|
||||
}
|
||||
}
|
||||
@@ -1629,78 +1594,23 @@ int mgcp_create_bind(const char *source_addr, int port, uint8_t dscp, uint8_t pr
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Bind RTP and RTCP port (helper function for mgcp_bind_net_rtp_port()) */
|
||||
static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
|
||||
struct mgcp_rtp_end *rtp_end, struct mgcp_endpoint *endp)
|
||||
{
|
||||
int rc, rtp_fd, rtcp_fd;
|
||||
|
||||
/* NOTE: The port that is used for RTCP is the RTP port incremented by one
|
||||
* (e.g. RTP-Port = 16000 ==> RTCP-Port = 16001) */
|
||||
|
||||
rc = mgcp_create_bind(source_addr, rtp_end->local_port, cfg->endp_dscp, cfg->endp_priority);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"failed to create RTP port: %s:%d\n",
|
||||
source_addr, rtp_end->local_port);
|
||||
goto cleanup0;
|
||||
}
|
||||
rtp_fd = rc;
|
||||
|
||||
rc = mgcp_create_bind(source_addr, rtp_end->local_port + 1, cfg->endp_dscp, cfg->endp_priority);
|
||||
if (rc < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"failed to create RTCP port: %s:%d\n",
|
||||
source_addr, rtp_end->local_port + 1);
|
||||
goto cleanup1;
|
||||
}
|
||||
rtcp_fd = rc;
|
||||
|
||||
if (osmo_iofd_register(rtp_end->rtp, rtp_fd) < 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"failed to register RTP port %d\n",
|
||||
rtp_end->local_port);
|
||||
goto cleanup2;
|
||||
}
|
||||
|
||||
if (osmo_iofd_register(rtp_end->rtcp, rtcp_fd) != 0) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR,
|
||||
"failed to register RTCP port %d\n",
|
||||
rtp_end->local_port + 1);
|
||||
goto cleanup3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup3:
|
||||
osmo_iofd_unregister(rtp_end->rtp);
|
||||
cleanup2:
|
||||
close(rtcp_fd);
|
||||
cleanup1:
|
||||
close(rtp_fd);
|
||||
cleanup0:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! bind RTP port to endpoint/connection.
|
||||
* \param[in] endp endpoint that holds the RTP connection.
|
||||
* \param[in] rtp_port port number to bind on.
|
||||
* \param[in] conn associated RTP connection.
|
||||
* \param[in] rtp_port port number to bind on.
|
||||
* \returns 0 on success, -1 on ERROR. */
|
||||
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
|
||||
struct mgcp_conn_rtp *conn)
|
||||
int mgcp_conn_rtp_bind_rtp_ports(struct mgcp_conn_rtp *conn_rtp, int rtp_port)
|
||||
{
|
||||
char name[512];
|
||||
struct mgcp_rtp_end *end;
|
||||
struct mgcp_conn *conn = conn_rtp->conn;
|
||||
struct mgcp_config *cfg = conn->endp->trunk->cfg;
|
||||
struct mgcp_rtp_end *end = &conn_rtp->end;
|
||||
int rc, rtp_fd, rtcp_fd;
|
||||
|
||||
snprintf(name, sizeof(name), "%s-%s", conn->conn->name, conn->conn->id);
|
||||
end = &conn->end;
|
||||
snprintf(name, sizeof(name), "%s-%s", conn->name, conn->id);
|
||||
|
||||
if ((end->rtp && osmo_iofd_get_fd(end->rtp) != -1) ||
|
||||
(end->rtcp && osmo_iofd_get_fd(end->rtcp) != -1)) {
|
||||
LOGPENDP(endp, DRTP, LOGL_ERROR, "%u was already bound on conn:%s\n",
|
||||
rtp_port, mgcp_conn_dump(conn->conn));
|
||||
|
||||
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "%u was already bound\n", rtp_port);
|
||||
/* Double bindings should never occour! Since we always allocate
|
||||
* connections dynamically and free them when they are not
|
||||
* needed anymore, there must be no previous binding leftover.
|
||||
@@ -1710,33 +1620,55 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
|
||||
}
|
||||
|
||||
end->local_port = rtp_port;
|
||||
end->rtp = osmo_iofd_setup(conn->conn, -1, name, OSMO_IO_FD_MODE_RECVFROM_SENDTO, &rtp_ioops, conn);
|
||||
end->rtp = osmo_iofd_setup(conn, -1, name, OSMO_IO_FD_MODE_RECVFROM_SENDTO, &rtp_ioops, conn_rtp);
|
||||
if (!end->rtp)
|
||||
return -EIO;
|
||||
goto free_iofd_ret;
|
||||
osmo_iofd_set_alloc_info(end->rtp, RTP_BUF_SIZE, 0);
|
||||
end->rtcp = osmo_iofd_setup(conn->conn, -1, name, OSMO_IO_FD_MODE_RECVFROM_SENDTO, &rtp_ioops, conn);
|
||||
if (!end->rtcp) {
|
||||
osmo_iofd_free(end->rtp);
|
||||
end->rtp = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
end->rtcp = osmo_iofd_setup(conn, -1, name, OSMO_IO_FD_MODE_RECVFROM_SENDTO, &rtp_ioops, conn_rtp);
|
||||
if (!end->rtcp)
|
||||
goto free_iofd_ret;
|
||||
osmo_iofd_set_alloc_info(end->rtcp, RTP_BUF_SIZE, 0);
|
||||
osmo_iofd_set_priv_nr(end->rtcp, 1); /* we use priv_nr as identifier for RTCP */
|
||||
|
||||
return bind_rtp(endp->trunk->cfg, conn->end.local_addr, end, endp);
|
||||
}
|
||||
/* NOTE: The port that is used for RTCP is the RTP port incremented by one
|
||||
* (e.g. RTP-Port = 16000 ==> RTCP-Port = 16001) */
|
||||
|
||||
/*! free allocated RTP and RTCP ports.
|
||||
* \param[in] end RTP end */
|
||||
void mgcp_free_rtp_port(struct mgcp_rtp_end *end)
|
||||
{
|
||||
if (end->rtp) {
|
||||
osmo_iofd_free(end->rtp);
|
||||
end->rtp = NULL;
|
||||
rc = mgcp_create_bind(end->local_addr, end->local_port, cfg->endp_dscp, cfg->endp_priority);
|
||||
if (rc < 0) {
|
||||
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "failed to create RTP port: %s:%d\n", end->local_addr, end->local_port);
|
||||
goto free_iofd_ret;
|
||||
}
|
||||
rtp_fd = rc;
|
||||
|
||||
rc = mgcp_create_bind(end->local_addr, end->local_port + 1, cfg->endp_dscp, cfg->endp_priority);
|
||||
if (rc < 0) {
|
||||
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "failed to create RTCP port: %s:%d\n", end->local_addr, end->local_port + 1);
|
||||
goto cleanup1;
|
||||
}
|
||||
rtcp_fd = rc;
|
||||
|
||||
if (osmo_iofd_register(end->rtp, rtp_fd) < 0) {
|
||||
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "failed to register RTP port %d\n", end->local_port);
|
||||
goto cleanup2;
|
||||
}
|
||||
|
||||
if (end->rtcp) {
|
||||
osmo_iofd_free(end->rtcp);
|
||||
end->rtcp = NULL;
|
||||
if (osmo_iofd_register(end->rtcp, rtcp_fd) != 0) {
|
||||
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "failed to register RTCP port %d\n", end->local_port + 1);
|
||||
goto cleanup3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup3:
|
||||
osmo_iofd_unregister(end->rtp);
|
||||
cleanup2:
|
||||
close(rtcp_fd);
|
||||
cleanup1:
|
||||
close(rtp_fd);
|
||||
free_iofd_ret:
|
||||
osmo_iofd_free(end->rtcp);
|
||||
end->rtcp = NULL;
|
||||
osmo_iofd_free(end->rtp);
|
||||
end->rtp = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@@ -211,6 +211,7 @@ osmux_handle_find_or_create(const struct mgcp_trunk *trunk, const struct osmo_so
|
||||
int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg)
|
||||
{
|
||||
int ret;
|
||||
size_t msg_len;
|
||||
|
||||
if (!conn->end.output_enabled) {
|
||||
rtpconn_osmux_rate_ctr_inc(conn, OSMUX_RTP_PACKETS_TX_DROPPED_CTR);
|
||||
@@ -234,15 +235,17 @@ int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg)
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg_len = msgb_length(msg);
|
||||
while ((ret = osmux_xfrm_input(conn->osmux.in, msg, conn->osmux.remote_cid)) > 0) {
|
||||
/* batch full, build and deliver it */
|
||||
osmux_xfrm_input_deliver(conn->osmux.in);
|
||||
}
|
||||
/* NOTE: At this point msg is now owned by osmux subsystem and may have been potentially freed. */
|
||||
if (ret < 0) {
|
||||
rtpconn_osmux_rate_ctr_inc(conn, OSMUX_RTP_PACKETS_TX_DROPPED_CTR);
|
||||
} else {
|
||||
rtpconn_osmux_rate_ctr_inc(conn, OSMUX_RTP_PACKETS_TX_CTR);
|
||||
rtpconn_osmux_rate_ctr_add(conn, OSMUX_AMR_OCTETS_TX_CTR, msgb_length(msg) - sizeof(struct rtp_hdr));
|
||||
rtpconn_osmux_rate_ctr_add(conn, OSMUX_AMR_OCTETS_TX_CTR, msg_len - sizeof(struct rtp_hdr));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -265,7 +268,7 @@ osmux_conn_lookup(const struct mgcp_trunk *trunk, uint8_t local_cid, const struc
|
||||
if (conn->type != MGCP_CONN_TYPE_RTP)
|
||||
continue;
|
||||
|
||||
conn_rtp = &conn->u.rtp;
|
||||
conn_rtp = mgcp_conn_get_conn_rtp(conn);
|
||||
if (!mgcp_conn_rtp_is_osmux(conn_rtp))
|
||||
continue;
|
||||
|
||||
@@ -495,6 +498,9 @@ int osmux_init(struct mgcp_trunk *trunk)
|
||||
int ret, fd;
|
||||
struct mgcp_config *cfg = trunk->cfg;
|
||||
|
||||
if (cfg->osmux.initialized)
|
||||
return 0;
|
||||
|
||||
/* So far we only support running on one trunk: */
|
||||
OSMO_ASSERT(trunk == mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID));
|
||||
|
||||
@@ -507,19 +513,19 @@ int osmux_init(struct mgcp_trunk *trunk)
|
||||
ret = mgcp_create_bind(cfg->osmux.local_addr_v4, cfg->osmux.local_port,
|
||||
cfg->endp_dscp, cfg->endp_priority);
|
||||
if (ret < 0) {
|
||||
LOGP(DOSMUX, LOGL_ERROR, "Cannot bind OSMUX IPv4 socket to %s:%u\n",
|
||||
cfg->osmux.local_addr_v4, cfg->osmux.local_port);
|
||||
LOGPTRUNK(trunk, DOSMUX, LOGL_ERROR, "Cannot bind OSMUX IPv4 socket to %s:%u\n",
|
||||
cfg->osmux.local_addr_v4, cfg->osmux.local_port);
|
||||
goto out_free_v4;
|
||||
}
|
||||
fd = ret;
|
||||
|
||||
ret = osmo_iofd_register(osmux_fd_v4, fd);
|
||||
if (ret < 0) {
|
||||
LOGP(DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv4 socket %s\n", osmo_sock_get_name2(fd));
|
||||
LOGPTRUNK(trunk, DOSMUX, LOGL_ERROR, "Cannot register OSMUX IPv4 socket %s\n", osmo_sock_get_name2(fd));
|
||||
close(fd);
|
||||
goto out_free_v4;
|
||||
}
|
||||
LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv4 socket listening on %s\n", osmo_sock_get_name2(fd));
|
||||
LOGPTRUNK(trunk, DOSMUX, LOGL_INFO, "OSMUX IPv4 socket listening on %s\n", osmo_sock_get_name2(fd));
|
||||
}
|
||||
|
||||
osmux_fd_v6 = osmo_iofd_setup(trunk, -1, "osmux_fd_v6", OSMO_IO_FD_MODE_RECVFROM_SENDTO, &osmux_ioops, trunk);
|
||||
@@ -531,8 +537,8 @@ int osmux_init(struct mgcp_trunk *trunk)
|
||||
ret = mgcp_create_bind(cfg->osmux.local_addr_v6, cfg->osmux.local_port,
|
||||
cfg->endp_dscp, cfg->endp_priority);
|
||||
if (ret < 0) {
|
||||
LOGP(DOSMUX, LOGL_ERROR, "Cannot bind OSMUX IPv6 socket to [%s]:%u\n",
|
||||
cfg->osmux.local_addr_v6, cfg->osmux.local_port);
|
||||
LOGPTRUNK(trunk, DOSMUX, LOGL_ERROR, "Cannot bind OSMUX IPv6 socket to [%s]:%u\n",
|
||||
cfg->osmux.local_addr_v6, cfg->osmux.local_port);
|
||||
goto out_free_v6;
|
||||
}
|
||||
fd = ret;
|
||||
@@ -543,9 +549,10 @@ int osmux_init(struct mgcp_trunk *trunk)
|
||||
close(fd);
|
||||
goto out_free_v6;
|
||||
}
|
||||
LOGP(DOSMUX, LOGL_INFO, "OSMUX IPv6 socket listening on %s\n", osmo_sock_get_name2(fd));
|
||||
LOGPTRUNK(trunk, DOSMUX, LOGL_INFO, "OSMUX IPv6 socket listening on %s\n", osmo_sock_get_name2(fd));
|
||||
}
|
||||
cfg->osmux.initialized = true;
|
||||
LOGPTRUNK(trunk, DOSMUX, LOGL_NOTICE, "OSMUX socket has been set up\n");
|
||||
return 0;
|
||||
|
||||
out_free_v6:
|
||||
@@ -557,6 +564,7 @@ out_free_v4:
|
||||
osmo_iofd_free(osmux_fd_v4);
|
||||
osmux_fd_v4 = NULL;
|
||||
out:
|
||||
LOGPTRUNK(trunk, DOSMUX, LOGL_ERROR, "Cannot init OSMUX\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -651,7 +659,7 @@ int conn_osmux_enable(struct mgcp_conn_rtp *conn)
|
||||
osmux_xfrm_output_set_rtp_ssrc(conn->osmux.out,
|
||||
(conn->osmux.remote_cid * rtp_ssrc_winlen) +
|
||||
(random() % rtp_ssrc_winlen));
|
||||
osmux_xfrm_output_set_rtp_pl_type(conn->osmux.out, conn->end.codec->payload_type);
|
||||
osmux_xfrm_output_set_rtp_pl_type(conn->osmux.out, conn->end.cset.codec->payload_type);
|
||||
osmux_xfrm_output_set_tx_cb(conn->osmux.out,
|
||||
scheduled_from_osmux_tx_rtp_cb, conn);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -93,7 +93,7 @@ static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = {
|
||||
[MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS] = { "mdcx:conn_opt", "connection options invalid." },
|
||||
[MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC] =
|
||||
{ "mdcx:no_remote_conn_desc", "no opposite end specified for connection." },
|
||||
[MGCP_MDCX_FAIL_START_RTP] = { "mdcx:start_rtp_failure", "failure to start RTP processing." },
|
||||
[MGCP_MDCX_FAIL_BIND_PORT] = { "mdcx:bind_port", "port bind failure." },
|
||||
[MGCP_MDCX_FAIL_AVAIL] = { "mdcx:unavailable", "endpoint unavailable." },
|
||||
};
|
||||
|
||||
|
||||
104
src/libosmo-mgcp/mgcp_rtp_end.c
Normal file
104
src/libosmo-mgcp/mgcp_rtp_end.c
Normal file
@@ -0,0 +1,104 @@
|
||||
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
|
||||
/*
|
||||
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009-2012 by On-Waves
|
||||
* (C) 2013-2024 by sysmocom - s.f.m.c. GmbH
|
||||
* 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/osmo_io.h>
|
||||
#include <osmocom/mgcp/mgcp_rtp_end.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <osmocom/mgcp/mgcp_conn.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_trunk.h>
|
||||
|
||||
/***********************
|
||||
* mgcp_rtp_end
|
||||
**********************/
|
||||
|
||||
void mgcp_rtp_end_init(struct mgcp_rtp_end *end, struct mgcp_conn_rtp *conn_rtp)
|
||||
{
|
||||
struct mgcp_trunk *trunk = conn_rtp->conn->endp->trunk;
|
||||
struct mgcp_config *cfg = trunk->cfg;
|
||||
|
||||
end->conn_rtp = conn_rtp;
|
||||
end->rtp = NULL;
|
||||
end->rtcp = NULL;
|
||||
memset(&end->addr, 0, sizeof(end->addr));
|
||||
end->rtcp_port = 0;
|
||||
|
||||
/* Set default values */
|
||||
end->frames_per_packet = 0; /* unknown */
|
||||
end->output_enabled = false;
|
||||
end->maximum_packet_time = -1;
|
||||
|
||||
end->force_aligned_timing = trunk->force_aligned_timing;
|
||||
end->force_constant_ssrc = trunk->force_constant_ssrc;
|
||||
end->rfc5993_hr_convert = trunk->rfc5993_hr_convert;
|
||||
|
||||
if (cfg->force_ptime) {
|
||||
end->packet_duration_ms = cfg->force_ptime;
|
||||
end->force_output_ptime = 1;
|
||||
} else {
|
||||
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
|
||||
}
|
||||
|
||||
/* Make sure codec table is reset */
|
||||
mgcp_codecset_reset(&end->cset);
|
||||
}
|
||||
|
||||
void mgcp_rtp_end_cleanup(struct mgcp_rtp_end *end)
|
||||
{
|
||||
mgcp_rtp_end_free_port(end);
|
||||
mgcp_codecset_reset(&end->cset);
|
||||
}
|
||||
|
||||
void mgcp_rtp_end_set_packet_duration_ms(struct mgcp_rtp_end *end, uint32_t packet_duration_ms)
|
||||
{
|
||||
if (end->force_output_ptime)
|
||||
return;
|
||||
end->packet_duration_ms = packet_duration_ms;
|
||||
}
|
||||
|
||||
bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end)
|
||||
{
|
||||
return (osmo_sockaddr_port(&rtp_end->addr.u.sa) != 0) &&
|
||||
(osmo_sockaddr_is_any(&rtp_end->addr) == 0);
|
||||
}
|
||||
|
||||
/*! free allocated RTP and RTCP ports.
|
||||
* \param[in] end RTP end */
|
||||
void mgcp_rtp_end_free_port(struct mgcp_rtp_end *end)
|
||||
{
|
||||
if (end->rtp) {
|
||||
osmo_iofd_free(end->rtp);
|
||||
end->rtp = NULL;
|
||||
}
|
||||
|
||||
if (end->rtcp) {
|
||||
osmo_iofd_free(end->rtcp);
|
||||
end->rtcp = NULL;
|
||||
}
|
||||
}
|
||||
@@ -199,7 +199,7 @@ static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp
|
||||
unsigned int pt;
|
||||
unsigned int count = 0;
|
||||
char delimiter;
|
||||
unsigned int amr_octet_aligned;
|
||||
unsigned int param_val;
|
||||
|
||||
memset(fmtp_param, 0, sizeof(*fmtp_param));
|
||||
|
||||
@@ -239,12 +239,30 @@ static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp
|
||||
if (delimiter == ';' || delimiter == ',')
|
||||
str_ptr[strlen(str_ptr) - 1] = '\0';
|
||||
|
||||
/* AMR octet aligned parameter (see also RFC 3267, section 8.3) */
|
||||
if (sscanf(param_str, "octet-align=%d", &amr_octet_aligned) == 1) {
|
||||
/* We support the following codec parameters:
|
||||
*
|
||||
* AMR: octet-align parameter of RFC 4867 section 8.3;
|
||||
* FR & EFR: tw-ts-001 parameter, Osmocom private;
|
||||
* HR: tw-ts-002 parameter, Osmocom private.
|
||||
*
|
||||
* tw-ts-001 and tw-ts-002 Osmocom-private parameters are used
|
||||
* only in the context of OsmoBSC driving an E1 MGW endpoint.
|
||||
*/
|
||||
if (sscanf(param_str, "octet-align=%d", ¶m_val) == 1) {
|
||||
fmtp_param->param.amr_octet_aligned_present = true;
|
||||
fmtp_param->param.amr_octet_aligned = false;
|
||||
if (amr_octet_aligned == 1)
|
||||
if (param_val == 1)
|
||||
fmtp_param->param.amr_octet_aligned = true;
|
||||
} else if (sscanf(param_str, "tw-ts-001=%d", ¶m_val) == 1) {
|
||||
fmtp_param->param.fr_efr_twts001_present = true;
|
||||
fmtp_param->param.fr_efr_twts001 = false;
|
||||
if (param_val == 1)
|
||||
fmtp_param->param.fr_efr_twts001 = true;
|
||||
} else if (sscanf(param_str, "tw-ts-002=%d", ¶m_val) == 1) {
|
||||
fmtp_param->param.hr_twts002_present = true;
|
||||
fmtp_param->param.hr_twts002 = false;
|
||||
if (param_val == 1)
|
||||
fmtp_param->param.hr_twts002 = true;
|
||||
}
|
||||
|
||||
param_str = strtok(NULL, " ");
|
||||
@@ -312,16 +330,13 @@ static struct mgcp_codec_param *param_by_pt(int pt, struct sdp_fmtp_param *fmtp_
|
||||
}
|
||||
|
||||
/*! 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.
|
||||
* \param[inout] p provided memory to store the parsing results.
|
||||
*
|
||||
* Note: In conn (conn->end) the function returns the packet duration,
|
||||
* 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)
|
||||
int mgcp_parse_sdp_data(struct mgcp_parse_data *p)
|
||||
{
|
||||
OSMO_ASSERT(p);
|
||||
struct mgcp_parse_sdp *sdp = &p->sdp;
|
||||
struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
|
||||
unsigned int codecs_used = 0;
|
||||
struct sdp_fmtp_param fmtp_params[MGCP_MAX_CODECS];
|
||||
@@ -331,19 +346,14 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
char *line;
|
||||
unsigned int i;
|
||||
void *tmp_ctx = talloc_new(NULL);
|
||||
struct mgcp_rtp_end *rtp;
|
||||
|
||||
int payload_type;
|
||||
int ptime, ptime2 = 0;
|
||||
char audio_name[64];
|
||||
int port, rc;
|
||||
|
||||
OSMO_ASSERT(endp);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(p);
|
||||
|
||||
rtp = &conn->end;
|
||||
memset(&codecs, 0, sizeof(codecs));
|
||||
mgcp_parse_sdp_init(sdp);
|
||||
|
||||
for_each_line(line, p->save) {
|
||||
switch (line[0]) {
|
||||
@@ -361,19 +371,19 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
|
||||
if (sscanf(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) {
|
||||
if (ptime2 > 0 && ptime2 != ptime)
|
||||
rtp->packet_duration_ms = 0;
|
||||
sdp->ptime = 0;
|
||||
else
|
||||
rtp->packet_duration_ms = ptime;
|
||||
sdp->ptime = ptime;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
|
||||
rtp->maximum_packet_time = ptime2;
|
||||
sdp->maxptime = ptime2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (strncmp("a=fmtp:", line, 6) == 0) {
|
||||
rc = fmtp_from_sdp(conn->conn, &fmtp_params[fmtp_used], line);
|
||||
rc = fmtp_from_sdp(tmp_ctx, &fmtp_params[fmtp_used], line);
|
||||
if (rc >= 0)
|
||||
fmtp_used++;
|
||||
break;
|
||||
@@ -382,33 +392,23 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
break;
|
||||
case 'm':
|
||||
rc = sscanf(line, "m=audio %d RTP/AVP", &port);
|
||||
if (rc == 1) {
|
||||
osmo_sockaddr_set_port(&rtp->addr.u.sa, port);
|
||||
rtp->rtcp_port = htons(port + 1);
|
||||
}
|
||||
if (rc == 1)
|
||||
sdp->rtp_port = port;
|
||||
|
||||
rc = pt_from_sdp(conn->conn, codecs,
|
||||
ARRAY_SIZE(codecs), line);
|
||||
rc = pt_from_sdp(tmp_ctx, codecs, ARRAY_SIZE(codecs), line);
|
||||
if (rc > 0)
|
||||
codecs_used = rc;
|
||||
break;
|
||||
case 'c':
|
||||
if (audio_ip_from_sdp(&rtp->addr, line) < 0) {
|
||||
if (audio_ip_from_sdp(&sdp->rem_addr, line) < 0) {
|
||||
talloc_free(tmp_ctx);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (endp)
|
||||
/* TODO: Check spec: We used the bare endpoint number before,
|
||||
* now we use the endpoint name as a whole? Is this allowed? */
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Unhandled SDP option: '%c'/%d on %s\n",
|
||||
line[0], line[0], endp->name);
|
||||
else
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Unhandled SDP option: '%c'/%d\n",
|
||||
line[0], line[0]);
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Unhandled SDP option: '%c'/%d on %s\n",
|
||||
line[0], line[0], p->epname);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -422,23 +422,22 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
/* Store parsed codec information */
|
||||
for (i = 0; i < codecs_used; i++) {
|
||||
codec_param = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
|
||||
rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line, codec_param);
|
||||
rc = mgcp_codecset_add_codec(&sdp->cset, codecs[i].payload_type, codecs[i].map_line, codec_param);
|
||||
if (rc < 0)
|
||||
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "failed to add codec\n");
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "%s: failed to add codec\n", p->epname);
|
||||
}
|
||||
|
||||
talloc_free(tmp_ctx);
|
||||
|
||||
LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
|
||||
"Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
|
||||
osmo_sockaddr_port(&rtp->addr.u.sa), osmo_sockaddr_ntop(&rtp->addr.u.sa, ipbuf),
|
||||
rtp->packet_duration_ms);
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"%s: Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
|
||||
p->epname, sdp->rtp_port, osmo_sockaddr_ntop(&sdp->rem_addr.u.sa, ipbuf), sdp->ptime);
|
||||
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,
|
||||
strlen(rtp->codecs[i].subtype_name) ? rtp->codecs[i].subtype_name : "unknown");
|
||||
sdp->cset.codecs[i].payload_type,
|
||||
strlen(sdp->cset.codecs[i].subtype_name) ? sdp->cset.codecs[i].subtype_name : "unknown");
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, " ");
|
||||
}
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, "\n");
|
||||
@@ -542,7 +541,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
OSMO_ASSERT(sdp);
|
||||
OSMO_ASSERT(addr);
|
||||
|
||||
codec = conn->end.codec;
|
||||
codec = conn->end.cset.codec;
|
||||
|
||||
audio_name = codec->audio_name;
|
||||
payload_type = codec->payload_type;
|
||||
@@ -578,7 +577,23 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
goto buffer_too_small;
|
||||
}
|
||||
|
||||
if (codec->param_present) {
|
||||
/* Include a=fmtp line in MGCP response only for AMR
|
||||
* octet-align parameter, not for tw-ts-* parameters.
|
||||
* Rationale:
|
||||
*
|
||||
* - tw-ts-* parameters exist meaningfully only for E1
|
||||
* endpoints driven by OsmoBSC, not in any other
|
||||
* context. libosmo-mgcp-client used by OsmoBSC
|
||||
* completely ignores all fmtp lines, has no code
|
||||
* to parse them at all.
|
||||
*
|
||||
* - The SDP we return is supposed to indicate what
|
||||
* _we_ accept, conceptually independent from what
|
||||
* the remote accepts. And we always accept TW-TS-001
|
||||
* and TW-TS-002 RTP formats going to E1 DL, whether
|
||||
* or not we send them in UL per client request.
|
||||
*/
|
||||
if (codec->param_present && codec->param.amr_octet_aligned_present) {
|
||||
fmtp_param.payload_type = payload_type;
|
||||
fmtp_param.param = codec->param;
|
||||
fmtp_params[0] = fmtp_param;
|
||||
|
||||
@@ -141,7 +141,7 @@ void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn)
|
||||
* keep this option open: */
|
||||
switch (conn->type) {
|
||||
case MGCP_CONN_TYPE_RTP:
|
||||
mgcp_format_stats_rtp(str, str_len, &conn->u.rtp);
|
||||
mgcp_format_stats_rtp(str, str_len, mgcp_conn_get_conn_rtp(conn));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -306,3 +306,43 @@ struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigne
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 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 in work on the first attempt in
|
||||
* general. In case of failure the next port is tried until the whole port
|
||||
* range is tried once. */
|
||||
int mgcp_trunk_allocate_conn_rtp_ports(struct mgcp_trunk *trunk, struct mgcp_conn_rtp *conn_rtp)
|
||||
{
|
||||
int i;
|
||||
struct mgcp_port_range *range;
|
||||
unsigned int tries;
|
||||
|
||||
OSMO_ASSERT(trunk);
|
||||
OSMO_ASSERT(conn_rtp);
|
||||
|
||||
range = &trunk->cfg->net_ports;
|
||||
|
||||
pthread_mutex_lock(&range->lock);
|
||||
/* attempt to find a port */
|
||||
tries = (range->range_end - range->range_start) / 2;
|
||||
for (i = 0; i < tries; ++i) {
|
||||
int rc;
|
||||
|
||||
if (range->last_port >= range->range_end)
|
||||
range->last_port = range->range_start;
|
||||
|
||||
rc = mgcp_conn_rtp_bind_rtp_ports(conn_rtp, range->last_port);
|
||||
|
||||
range->last_port += 2;
|
||||
if (rc == 0) {
|
||||
pthread_mutex_unlock(&range->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
pthread_mutex_unlock(&range->lock);
|
||||
LOGPCONN(conn_rtp->conn, DLMGCP, LOGL_ERROR,
|
||||
"Allocating a RTP/RTCP port failed %u times.\n", tries);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
struct mgcp_rtp_state *state = &conn->state;
|
||||
struct mgcp_rtp_end *end = &conn->end;
|
||||
struct mgcp_rtp_codec *codec = end->codec;
|
||||
struct mgcp_rtp_codec *codec = end->cset.codec;
|
||||
struct rate_ctr *tx_packets, *tx_bytes;
|
||||
struct rate_ctr *rx_packets, *rx_bytes;
|
||||
struct rate_ctr *dropped_packets;
|
||||
@@ -254,7 +254,8 @@ static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp,
|
||||
* connection types (E1) as soon as
|
||||
* the implementation is available */
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
dump_rtp_end(vty, &conn->u.rtp);
|
||||
struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
|
||||
dump_rtp_end(vty, conn_rtp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -856,7 +857,7 @@ DEFUN_USRATTR(cfg_mgcp_patch_rtp_ssrc,
|
||||
{
|
||||
struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
|
||||
OSMO_ASSERT(trunk);
|
||||
trunk->force_constant_ssrc = 1;
|
||||
trunk->force_constant_ssrc = true;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -867,7 +868,7 @@ DEFUN_USRATTR(cfg_mgcp_no_patch_rtp_ssrc,
|
||||
{
|
||||
struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
|
||||
OSMO_ASSERT(trunk);
|
||||
trunk->force_constant_ssrc = 0;
|
||||
trunk->force_constant_ssrc = false;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -922,7 +923,7 @@ DEFUN_USRATTR(cfg_mgcp_no_patch_rtp,
|
||||
{
|
||||
struct mgcp_trunk *trunk = mgcp_trunk_by_num(g_cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
|
||||
OSMO_ASSERT(trunk);
|
||||
trunk->force_constant_ssrc = 0;
|
||||
trunk->force_constant_ssrc = false;
|
||||
trunk->force_aligned_timing = 0;
|
||||
trunk->rfc5993_hr_convert = false;
|
||||
return CMD_SUCCESS;
|
||||
@@ -1196,7 +1197,7 @@ DEFUN_USRATTR(cfg_trunk_patch_rtp_ssrc,
|
||||
"rtp-patch ssrc", RTP_PATCH_STR "Force a fixed SSRC\n")
|
||||
{
|
||||
struct mgcp_trunk *trunk = vty->index;
|
||||
trunk->force_constant_ssrc = 1;
|
||||
trunk->force_constant_ssrc = true;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1206,7 +1207,7 @@ DEFUN_USRATTR(cfg_trunk_no_patch_rtp_ssrc,
|
||||
"no rtp-patch ssrc", NO_STR RTP_PATCH_STR "Force a fixed SSRC\n")
|
||||
{
|
||||
struct mgcp_trunk *trunk = vty->index;
|
||||
trunk->force_constant_ssrc = 0;
|
||||
trunk->force_constant_ssrc = false;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1256,7 +1257,7 @@ DEFUN_USRATTR(cfg_trunk_no_patch_rtp,
|
||||
"no rtp-patch", NO_STR RTP_PATCH_STR)
|
||||
{
|
||||
struct mgcp_trunk *trunk = vty->index;
|
||||
trunk->force_constant_ssrc = 0;
|
||||
trunk->force_constant_ssrc = false;
|
||||
trunk->force_aligned_timing = 0;
|
||||
trunk->rfc5993_hr_convert = false;
|
||||
return CMD_SUCCESS;
|
||||
@@ -1357,10 +1358,11 @@ DEFUN(loop_conn,
|
||||
endp = trunk->endpoints[endp_no];
|
||||
int loop = atoi(argv[2]);
|
||||
llist_for_each_entry(conn, &endp->conns, entry) {
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP)
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
/* Handle it like a MDCX, switch on SSRC patching if enabled */
|
||||
mgcp_rtp_end_config(endp, 1, &conn->u.rtp.end);
|
||||
else {
|
||||
struct mgcp_conn_rtp *conn_rtp = mgcp_conn_get_conn_rtp(conn);
|
||||
conn_rtp->state.patch.patch_ssrc = true;
|
||||
} else {
|
||||
/* FIXME: Introduce support for other connection (E1)
|
||||
* types when implementation is available */
|
||||
vty_out(vty, "%%Can't enable SSRC patching,"
|
||||
@@ -1418,7 +1420,7 @@ DEFUN(tap_rtp,
|
||||
endp = trunk->endpoints[endp_no];
|
||||
|
||||
conn_id = argv[2];
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
|
||||
if (!conn) {
|
||||
vty_out(vty, "Conn ID %s is invalid.%s",
|
||||
conn_id, VTY_NEWLINE);
|
||||
|
||||
@@ -27,6 +27,7 @@ osmo_mgw_SOURCES = \
|
||||
osmo_mgw_LDADD = \
|
||||
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOCODEC_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOCTRL_LIBS) \
|
||||
|
||||
@@ -249,17 +249,6 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mgcp_vty_is_config_node(struct vty *vty, int node)
|
||||
{
|
||||
switch (node) {
|
||||
case CONFIG_NODE:
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int mgcp_vty_go_parent(struct vty *vty)
|
||||
{
|
||||
switch (vty->node) {
|
||||
@@ -269,7 +258,7 @@ int mgcp_vty_go_parent(struct vty *vty)
|
||||
break;
|
||||
case MGCP_NODE:
|
||||
default:
|
||||
if (mgcp_vty_is_config_node(vty, vty->node))
|
||||
if (vty->node != CONFIG_NODE)
|
||||
vty->node = CONFIG_NODE;
|
||||
else
|
||||
vty->node = ENABLE_NODE;
|
||||
@@ -310,7 +299,6 @@ static struct vty_app_info vty_info = {
|
||||
.name = "OsmoMGW",
|
||||
.version = PACKAGE_VERSION,
|
||||
.go_parent_cb = mgcp_vty_go_parent,
|
||||
.is_config_node = mgcp_vty_is_config_node,
|
||||
};
|
||||
|
||||
static const struct log_info_cat log_categories[] = {
|
||||
|
||||
@@ -36,6 +36,7 @@ mgcp_test_SOURCES = \
|
||||
mgcp_test_LDADD = \
|
||||
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOCODEC_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOABIS_LIBS) \
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
#include <math.h>
|
||||
#include <ctype.h>
|
||||
|
||||
void *tall_mgw_ctx;
|
||||
|
||||
char *strline_r(char *str, char **saveptr);
|
||||
|
||||
const char *strline_test_data =
|
||||
@@ -965,7 +967,7 @@ static void test_messages(void)
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
|
||||
conn = mgcp_conn_get_rtp(endp, "1");
|
||||
conn = mgcp_endp_get_conn_rtp(endp, "1");
|
||||
if (conn) {
|
||||
OSMO_ASSERT(conn);
|
||||
|
||||
@@ -1023,14 +1025,14 @@ static void test_messages(void)
|
||||
fprintf(stderr, "endpoint:%s: "
|
||||
"payload type %d (expected %d)\n",
|
||||
last_endpoint,
|
||||
conn->end.codec->payload_type, t->ptype);
|
||||
conn->end.cset.codec->payload_type, t->ptype);
|
||||
|
||||
if (t->ptype != PTYPE_IGNORE)
|
||||
OSMO_ASSERT(conn->end.codec->payload_type ==
|
||||
OSMO_ASSERT(conn->end.cset.codec->payload_type ==
|
||||
t->ptype);
|
||||
|
||||
/* Reset them again for next test */
|
||||
conn->end.codec->payload_type = PTYPE_NONE;
|
||||
conn->end.cset.codec->payload_type = PTYPE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1209,7 +1211,7 @@ static void test_packet_loss_calc(void)
|
||||
mgcp_conn_alloc(NULL, &endp, MGCP_CONN_TYPE_RTP,
|
||||
"test-connection");
|
||||
OSMO_ASSERT(_conn);
|
||||
conn = mgcp_conn_get_rtp(&endp, _conn->id);
|
||||
conn = mgcp_endp_get_conn_rtp(&endp, _conn->id);
|
||||
OSMO_ASSERT(conn);
|
||||
state = &conn->state;
|
||||
packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
|
||||
@@ -1230,7 +1232,7 @@ static void test_packet_loss_calc(void)
|
||||
pl_test_dat[i].expected);
|
||||
}
|
||||
|
||||
mgcp_conn_free_all(&endp);
|
||||
mgcp_endp_free_conn_all(&endp);
|
||||
}
|
||||
|
||||
talloc_free(trunk);
|
||||
@@ -1461,13 +1463,13 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
_conn = mgcp_conn_alloc(NULL, &endp, MGCP_CONN_TYPE_RTP,
|
||||
"test-connection");
|
||||
OSMO_ASSERT(_conn);
|
||||
conn = mgcp_conn_get_rtp(&endp, _conn->id);
|
||||
conn = mgcp_endp_get_conn_rtp(&endp, _conn->id);
|
||||
OSMO_ASSERT(conn);
|
||||
|
||||
rtp = &conn->end;
|
||||
|
||||
OSMO_ASSERT(mgcp_codec_add(conn, PTYPE_UNDEFINED, "AMR/8000/1", NULL) == 0);
|
||||
rtp->codec = &rtp->codecs[0];
|
||||
OSMO_ASSERT(mgcp_codecset_add_codec(&conn->end.cset, PTYPE_UNDEFINED, "AMR/8000/1", NULL) == 0);
|
||||
rtp->cset.codec = &rtp->cset.codecs[0];
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
|
||||
struct rtp_packet_info *info = test_rtp_packets1 + i;
|
||||
@@ -1479,7 +1481,6 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
OSMO_ASSERT(info->len >= 0);
|
||||
msg->l3h = msgb_put(msg, info->len);
|
||||
memcpy((char*)msgb_l3(msg), info->data, info->len);
|
||||
mgcp_rtp_end_config(&endp, 1, rtp);
|
||||
|
||||
mgcp_patch_and_count(&endp, &state, rtp, &addr, msg);
|
||||
|
||||
@@ -1513,7 +1514,7 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
}
|
||||
|
||||
force_monotonic_time_us = -1;
|
||||
mgcp_conn_free_all(&endp);
|
||||
mgcp_endp_free_conn_all(&endp);
|
||||
talloc_free(trunk);
|
||||
talloc_free(cfg);
|
||||
}
|
||||
@@ -1548,9 +1549,9 @@ static void test_multilple_codec(void)
|
||||
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/1@mgw") == 0);
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 18);
|
||||
OSMO_ASSERT(conn->end.cset.codec->payload_type == 18);
|
||||
|
||||
/* Allocate 2@mgw with three codecs, last one ignored */
|
||||
last_endpoint[0] = '\0';
|
||||
@@ -1564,9 +1565,9 @@ static void test_multilple_codec(void)
|
||||
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/2@mgw") == 0);
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 18);
|
||||
OSMO_ASSERT(conn->end.cset.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
|
||||
@@ -1585,9 +1586,9 @@ static void test_multilple_codec(void)
|
||||
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/3@mgw") == 0);
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 0);
|
||||
OSMO_ASSERT(conn->end.cset.codec->payload_type == 0);
|
||||
|
||||
/* Allocate 4@mgw with a single codec */
|
||||
last_endpoint[0] = '\0';
|
||||
@@ -1601,9 +1602,9 @@ static void test_multilple_codec(void)
|
||||
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/4@mgw") == 0);
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 18);
|
||||
OSMO_ASSERT(conn->end.cset.codec->payload_type == 18);
|
||||
|
||||
/* Allocate 5@mgw and let osmo-mgw pick a codec from the list */
|
||||
last_endpoint[0] = '\0';
|
||||
@@ -1617,9 +1618,9 @@ static void test_multilple_codec(void)
|
||||
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 0);
|
||||
OSMO_ASSERT(conn->end.cset.codec->payload_type == 0);
|
||||
|
||||
inp = create_msg(MDCX_NAT_DUMMY, conn_id);
|
||||
last_endpoint[0] = '\0';
|
||||
@@ -1629,9 +1630,9 @@ static void test_multilple_codec(void)
|
||||
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 3);
|
||||
OSMO_ASSERT(conn->end.cset.codec->payload_type == 3);
|
||||
OSMO_ASSERT(osmo_sockaddr_port(&conn->end.addr.u.sa) == 16434);
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
inet_aton("8.8.8.8", &addr);
|
||||
@@ -1646,7 +1647,7 @@ static void test_multilple_codec(void)
|
||||
talloc_free(endp->last_response);
|
||||
talloc_free(endp->last_trans);
|
||||
endp->last_response = endp->last_trans = NULL;
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(!conn);
|
||||
|
||||
last_endpoint[0] = '\0';
|
||||
@@ -1660,9 +1661,9 @@ static void test_multilple_codec(void)
|
||||
OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);
|
||||
endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
|
||||
OSMO_ASSERT(endp);
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
conn = mgcp_endp_get_conn_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 0);
|
||||
OSMO_ASSERT(conn->end.cset.codec->payload_type == 0);
|
||||
|
||||
mgcp_endpoints_release(trunk);
|
||||
talloc_free(cfg);
|
||||
@@ -1689,7 +1690,7 @@ static void test_no_cycle(void)
|
||||
_conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP,
|
||||
"test-connection");
|
||||
OSMO_ASSERT(_conn);
|
||||
conn = mgcp_conn_get_rtp(endp, _conn->id);
|
||||
conn = mgcp_endp_get_conn_rtp(endp, _conn->id);
|
||||
OSMO_ASSERT(conn);
|
||||
|
||||
OSMO_ASSERT(conn->state.stats.initialized == 0);
|
||||
@@ -2195,7 +2196,7 @@ static bool codec_decision(struct mgcp_conn_rtp *conn, unsigned int index_conn_s
|
||||
int payload_type_conn_dst;
|
||||
|
||||
printf(" - mgcp_codec_decide(&conn[%u], &conn[%u]):\n", index_conn_src, index_conn_dst);
|
||||
if (mgcp_codec_decide(&conn[index_conn_src], &conn[index_conn_dst]) != 0) {
|
||||
if (mgcp_codecset_decide(&conn[index_conn_src].end.cset, &conn[index_conn_dst].end.cset) != 0) {
|
||||
if (expect->payload_type_map[index_conn_src] == -EINVAL
|
||||
&& expect->payload_type_map[index_conn_dst] == -EINVAL)
|
||||
printf(" codec decision failed (expected)!\n");
|
||||
@@ -2205,19 +2206,19 @@ static bool codec_decision(struct mgcp_conn_rtp *conn, unsigned int index_conn_s
|
||||
}
|
||||
} else {
|
||||
printf(" Codec decision result:\n");
|
||||
if (conn[index_conn_src].end.codec) {
|
||||
payload_type_conn_src = conn[index_conn_src].end.codec->payload_type;
|
||||
if (conn[index_conn_src].end.cset.codec) {
|
||||
payload_type_conn_src = conn[index_conn_src].end.cset.codec->payload_type;
|
||||
printf(" conn[%u]: codec:%s, pt:%d\n",
|
||||
index_conn_src, conn[index_conn_src].end.codec->subtype_name, payload_type_conn_src);
|
||||
index_conn_src, conn[index_conn_src].end.cset.codec->subtype_name, payload_type_conn_src);
|
||||
} else {
|
||||
payload_type_conn_src = -EINVAL;
|
||||
printf(" conn[%u]: codec:none, pt:none\n", index_conn_src);
|
||||
}
|
||||
|
||||
if (conn[index_conn_dst].end.codec) {
|
||||
payload_type_conn_dst = conn[index_conn_dst].end.codec->payload_type;
|
||||
if (conn[index_conn_dst].end.cset.codec) {
|
||||
payload_type_conn_dst = conn[index_conn_dst].end.cset.codec->payload_type;
|
||||
printf(" conn[%u]: codec:%s, pt:%d\n",
|
||||
index_conn_dst, conn[index_conn_dst].end.codec->subtype_name,
|
||||
index_conn_dst, conn[index_conn_dst].end.cset.codec->subtype_name,
|
||||
payload_type_conn_dst);
|
||||
} else {
|
||||
payload_type_conn_dst = -EINVAL;
|
||||
@@ -2265,8 +2266,8 @@ static void test_mgcp_codec_decide(void)
|
||||
if (!codec->audio_name)
|
||||
break;
|
||||
|
||||
rc = mgcp_codec_add(&conn[conn_i], codec->payload_type, codec->audio_name,
|
||||
codec->param);
|
||||
rc = mgcp_codecset_add_codec(&conn[conn_i].end.cset, codec->payload_type,
|
||||
codec->audio_name, codec->param);
|
||||
|
||||
printf(" %2d: %3d %s%s -> rc=%d\n", c, codec->payload_type, codec->audio_name,
|
||||
codec->param ?
|
||||
@@ -2324,7 +2325,7 @@ void test_conn_id_matching(void)
|
||||
|
||||
INIT_LLIST_HEAD(&endp.conns);
|
||||
|
||||
conn = talloc_zero(NULL, struct mgcp_conn);
|
||||
conn = talloc_zero(tall_mgw_ctx, struct mgcp_conn);
|
||||
OSMO_ASSERT(conn);
|
||||
osmo_strlcpy(conn->id, conn_id_generated, sizeof(conn->id));
|
||||
llist_add(&conn->entry, &endp.conns);
|
||||
@@ -2332,7 +2333,7 @@ void test_conn_id_matching(void)
|
||||
for (i = 0; i < ARRAY_SIZE(conn_id_request); i++) {
|
||||
const char *needle = conn_id_request[i];
|
||||
printf("needle='%s' ", needle);
|
||||
conn_match = mgcp_conn_get(&endp, needle);
|
||||
conn_match = mgcp_endp_get_conn(&endp, needle);
|
||||
OSMO_ASSERT(conn_match);
|
||||
printf("found '%s'\n", conn_match->id);
|
||||
OSMO_ASSERT(conn_match == conn);
|
||||
|
||||
Reference in New Issue
Block a user