Compare commits

..

2 Commits

Author SHA1 Message Date
Philipp Maier
ed7c4fcfc3 mgcp_codec: do not differentiate between oa and bwe when comparing codec
AMR that has the payload format bandwith-efficient is the same codec as
AMR that has the payload format octet-aligned. Its the same codec, and a
comparison of the codec info with the function codecs_same() should
return true (=equal).

The affected function codecs_same() is used by mgcp_codec_pt_translate().
When the egress payload type number is looked up, the ingress and egress
codec information is compared. When one end is using AMR in
bandwith-efficient format and the other end is using it in
octet-alingned format. Then the codec still must be recognized as the
same codec. Othersiwse the payload type number translation would not
work, even though the codec is the same on both sides.

Change-Id: I64731570c287a75d39c79c10e1bc09a37bdd54d6
Related: SYS#5834
2022-02-16 17:59:43 +01:00
Pau Espin Pedrol
3babd1a803 Make function amr_is_octet_aligned publicly available
it will be used by mgcp_iuup.c in follow-up patch.

Change-Id: Iffaf90c1f713feef0c609a7581a346f5f28141d9
2022-02-16 17:59:18 +01:00
49 changed files with 1072 additions and 2289 deletions

View File

@@ -1,6 +1,6 @@
To run the configuration parsing and output (VTY) test suite, first install
https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests
git://git.osmocom.org/python/osmo-python-tests
and pass the following configure options here:

View File

@@ -24,6 +24,3 @@
# If any interfaces have been removed or changed since the last public release, a=0.
#
#library what description / commit summary line
libosmo-netif >1.2.0 OSMUX_DEFAULT_PORT, osmux_xfrm_output_*, osmux_xfrm_input_*
libosmocore >1.7.0 osmo_sockaddr_is_any()
libmgcp-client NEW APIs mgcp_client_pool_member_...(), mgcp_client_pool_config_write()

View File

@@ -44,13 +44,13 @@ AC_SEARCH_LIBS([dlsym], [dl dld], [LIBRARY_DLSYM="$LIBS";LIBS=""])
AC_SUBST(LIBRARY_DLSYM)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.7.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.7.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.7.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.7.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.2.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.3.0)
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.3.0)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.1.0)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.2.0)
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.2.0)
CFLAGS="$CFLAGS -pthread"
LDFLAGS="$LDFLAGS -pthread"
@@ -79,7 +79,6 @@ AC_ARG_ENABLE(werror,
if test x"$werror" = x"yes"
then
WERROR_FLAGS="-Werror"
WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition"
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
CFLAGS="$CFLAGS $WERROR_FLAGS"
@@ -113,7 +112,7 @@ if test "x$enable_ext_tests" = "xyes" ; then
AM_PATH_PYTHON
AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmotestvty.py,yes)
if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then
AC_MSG_ERROR([Please install https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests to run the VTY/CTRL tests.])
AC_MSG_ERROR([Please install git://osmocom.org/python/osmo-python-tests to run the VTY/CTRL tests.])
fi
fi
AC_MSG_CHECKING([whether to enable VTY/CTRL tests])

View File

@@ -29,14 +29,14 @@ BuildRequires: pkgconfig >= 0.20
%if 0%{?suse_version}
BuildRequires: systemd-rpm-macros
%endif
BuildRequires: pkgconfig(libosmo-netif) >= 1.2.0
BuildRequires: pkgconfig(libosmocore) >= 1.7.0
BuildRequires: pkgconfig(libosmoctrl) >= 1.7.0
BuildRequires: pkgconfig(libosmogsm) >= 1.7.0
BuildRequires: pkgconfig(libosmovty) >= 1.7.0
BuildRequires: pkgconfig(libosmocoding) >= 1.7.0
BuildRequires: pkgconfig(libosmoabis) >= 1.3.0
BuildRequires: pkgconfig(libosmotrau) >= 1.3.0
BuildRequires: pkgconfig(libosmo-netif) >= 1.1.0
BuildRequires: pkgconfig(libosmocore) >= 1.6.0
BuildRequires: pkgconfig(libosmoctrl) >= 1.6.0
BuildRequires: pkgconfig(libosmogsm) >= 1.6.0
BuildRequires: pkgconfig(libosmovty) >= 1.6.0
BuildRequires: pkgconfig(libosmocoding) >= 1.6.0
BuildRequires: pkgconfig(libosmoabis) >= 1.2.0
BuildRequires: pkgconfig(libosmotrau) >= 1.2.0
%{?systemd_requires}
%description

View File

@@ -3,16 +3,9 @@ Description=Osmocom Media Gateway (MGW)
[Service]
Type=simple
StateDirectory=osmocom
WorkingDirectory=%S/osmocom
Restart=always
ExecStart=/usr/bin/osmo-mgw -s -c /etc/osmocom/osmo-mgw.cfg
RestartSec=2
# CPU scheduling policy:
CPUSchedulingPolicy=rr
# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
CPUSchedulingPriority=1
# See sched(7) for further details on real-time policies and priorities
[Install]
WantedBy=multi-user.target

44
debian/changelog vendored
View File

@@ -1,47 +1,3 @@
osmo-mgw (1.10.0) unstable; urgency=medium
[ Eric ]
* adjust mgcp response context
* rework message handling
* add a lock-free bounded spsc interthread queue
* clang-format: remove foreach macros
* fix mgcp_conn_free_all ubsan complaints
[ Philipp Maier ]
* configuration: point out difference between trunk-nr and e1 line nr
* mgcp_client: add new codec IUFP as VND.3GPP.IUFP
* mgcp_codec: do not differentiate between oa and bwe when comparing codec
* mgcp_network: do not try to convert RTCP packets
* mgcp_network: fix typo RTPC -> RTCP
[ Oliver Smith ]
* treewide: remove FSF address
[ Pau Espin Pedrol ]
* mgcp_network.c: Set proper CRC Header for ACK Initialization
* cosmetic: Rename variable payload=>payload_type
* mgcp_network.c: Fix byte alignment of CRC Header for ACK Initialization
* Define mgcp_rtp_end.output_enabled as bool
* cosmetic: mgcp_codec.c: Fix typo in comment
* Drop unneeded ax_check_compile_flag.m4
* Make function amr_is_octet_aligned publicly available
* Initial IuUP support using proper FSMs
* IuUP: Support RFCI ID != RFCI Index
* iuup: Fix caps in logging message
* iuup: Check for IuUP Initialization retrans
[ Alexander Couzens ]
* doc/overview: fix wrong project page link
[ Vadim Yanitskiy ]
* tests: use 'check_PROGRAMS' instead of 'noinst_PROGRAMS'
* libosmo-mgcp: e1: fix memleaks in e1_recv_cb()
[ Harald Welte ]
* update git URLs (git -> https; gitea)
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 28 Jun 2022 18:50:25 +0200
osmo-mgw (1.9.0) unstable; urgency=medium
[ Harald Welte ]

12
debian/control vendored
View File

@@ -6,13 +6,13 @@ Build-Depends: debhelper (>=9),
dh-autoreconf,
pkg-config,
autotools-dev,
libosmocore-dev (>= 1.7.0),
libosmo-netif-dev (>= 1.2.0),
libosmo-abis-dev (>= 1.3.0),
osmo-gsm-manuals-dev (>= 1.3.0)
libosmocore-dev (>= 1.6.0),
libosmo-netif-dev (>= 1.1.0),
libosmo-abis-dev (>= 1.2.0),
osmo-gsm-manuals-dev (>= 1.2.0)
Standards-Version: 3.9.8
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
Vcs-Git: git://git.osmocom.org/osmo-mgw.git
Vcs-Browser: https://git.osmocom.org/osmo-mgw/
Homepage: https://osmocom.org/projects/osmo-mgw
Package: osmo-mgw

2
debian/copyright vendored
View File

@@ -1,6 +1,6 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: osmo-mgw
Source: https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw
Source: git://git.osmocom.org/osmo-mgw
Files: *
Copyright: 2009-2014 On-Waves

View File

@@ -107,8 +107,8 @@ We are planning to add further endpoint types for:
You can find the OsmoMGW issue tracker and wiki online at
- https://osmocom.org/projects/osmo-mgw
- https://osmocom.org/projects/osmo-mgw/wiki
- https://osmocom.org/projects/osmomgw
- https://osmocom.org/projects/osmomgw/wiki
RFC 3435 for MGCP is located at

View File

@@ -6,11 +6,11 @@ noinst_HEADERS = \
mgcp_endp.h \
mgcp_sdp.h \
mgcp_codec.h \
mgcp_ctrl.h \
mgcp_trunk.h \
debug.h \
mgcp_ratectr.h \
mgcp_e1.h \
mgcp_network.h \
mgcp_protocol.h \
mgcp_iuup.h \
$(NULL)

View File

@@ -30,7 +30,6 @@
enum {
DRTP,
DE1,
DOSMUX,
Debug_LastEntry,
};

View File

@@ -29,7 +29,6 @@
#include <osmocom/core/logging.h>
#include <osmocom/mgcp/mgcp_common.h>
#include <osmocom/mgcp/osmux.h>
#include <arpa/inet.h>
#include <sys/types.h>
@@ -157,26 +156,24 @@ struct mgcp_config {
enum mgcp_role role;
struct {
/* Osmux usage policy: */
enum osmux_usage usage;
/* addr to bind the server to */
char *local_addr_v4;
char *local_addr_v6;
/* osmux port */
uint16_t local_port;
/* The osmux socket is allocated on demand (1st time used).
* This tells us if the osmux socket is already initialized. */
bool initialized;
/* osmux batch factor: from 1 to 4 maximum */
int batch_factor;
/* osmux batch size (in bytes) */
int batch_size;
/* Pad circuit with dummy AMR frames if no payload to transmit is available */
bool dummy_padding;
/* Whether peer is behind NAT (Retrieve remote addr from 1st received Osmux packet) */
bool peer_behind_nat;
} osmux;
/* osmux translator: 0 means disabled, 1 means enabled */
int osmux;
/* addr to bind the server to */
char osmux_addr[INET6_ADDRSTRLEN];
/* The BSC-NAT may ask for enabling osmux on demand. This tells us if
* the osmux socket is already initialized.
*/
int osmux_init;
/* osmux batch factor: from 1 to 4 maximum */
int osmux_batch;
/* osmux batch size (in bytes) */
int osmux_batch_size;
/* osmux port */
uint16_t osmux_port;
/* Pad circuit with dummy messages until we see the first voice
* message.
*/
uint16_t osmux_dummy;
/* domain name of the media gateway */
char domain[255+1];
@@ -210,4 +207,4 @@ int mgcp_send_reset_all(struct mgcp_config *cfg);
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port, uint8_t dscp,
uint8_t prio);
int mgcp_udp_send(int fd, const struct osmo_sockaddr *addr, const char *buf, int len);
int mgcp_udp_send(int fd, struct osmo_sockaddr *addr, int port, const char *buf, int len);

View File

@@ -17,5 +17,4 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn);
int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type);
const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn,
const char *subtype_name, unsigned int match_nr);
bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec);
bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec);

View File

@@ -28,7 +28,6 @@
#include <osmocom/mgcp/osmux.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/gsm/iuup.h>
#include <inttypes.h>
#define LOGPCONN(conn, cat, level, fmt, args...) \
@@ -46,8 +45,8 @@ LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
/* Specific rtp connection type (see struct mgcp_conn_rtp) */
enum mgcp_conn_rtp_type {
MGCP_RTP_DEFAULT = 0,
MGCP_RTP_OSMUX,
MGCP_RTP_IUUP,
MGCP_OSMUX_BSC,
MGCP_OSMUX_BSC_NAT,
};
/*! Connection type, specifies which member of the union "u" in mgcp_conn
@@ -79,31 +78,22 @@ struct mgcp_conn_rtp {
struct {
/* Osmux state: disabled, activating, active */
enum osmux_state state;
/* Is local_cid holding valid data? is it allocated from pool? */
bool local_cid_allocated;
/* Allocated local Osmux circuit ID for this conn */
uint8_t local_cid;
/* Is remote_cid holding valid data? was it already received from client? */
bool remote_cid_present;
/* Received remote Osmux circuit ID for this conn */
uint8_t remote_cid;
/* handle to batch messages, shared (refcounted) among several conns */
/* Is cid holding valid data? is it allocated from pool? */
bool cid_allocated;
/* Allocated Osmux circuit ID for this conn */
uint8_t cid;
/* handle to batch messages */
struct osmux_in_handle *in;
/* handle to unbatch messages, one allocated and owned per conn */
struct osmux_out_handle *out;
/* statistics: */
struct rate_ctr_group *ctrg;
/* handle to unbatch messages */
struct osmux_out_handle out;
/* statistics */
struct {
uint32_t chunks;
uint32_t octets;
} stats;
} osmux;
struct {
struct osmo_iuup_instance *iui;
bool active_init; /* true: Send IuUP Init */
int rfci_id_no_data; /* RFCI Id for RFCI NO_DATA (-1 if not available) */
bool configured;
struct osmo_iuup_rnl_prim *init_ind;
} iuup;
struct rate_ctr_group *ctrg;
struct rate_ctr_group *rate_ctr_group;
};
/*! MGCP connection (untyped) */
@@ -145,12 +135,12 @@ struct mgcp_conn {
enum {
IN_STREAM_ERR_TSTMP_CTR,
OUT_STREAM_ERR_TSTMP_CTR,
RTP_PACKETS_RX_CTR,
RTP_OCTETS_RX_CTR,
RTP_PACKETS_TX_CTR,
RTP_OCTETS_TX_CTR,
RTP_DROPPED_PACKETS_CTR,
RTP_NUM_CONNECTIONS,
RTP_PACKETS_RX_CTR,
RTP_OCTETS_RX_CTR,
RTP_PACKETS_TX_CTR,
RTP_OCTETS_TX_CTR,
RTP_DROPPED_PACKETS_CTR,
RTP_NUM_CONNECTIONS,
};
/* RTP per-connection statistics. Instances of the corresponding rate counter group
@@ -163,7 +153,7 @@ static const struct rate_ctr_desc mgcp_conn_rate_ctr_desc[] = {
[RTP_OCTETS_RX_CTR] = {"rtp:octets_rx", "Inbound rtp octets."},
[RTP_PACKETS_TX_CTR] = {"rtp:packets_tx", "Outbound rtp packets."},
[RTP_OCTETS_TX_CTR] = {"rtp:octets_tx", "Outbound rtp octets."},
[RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "Dropped rtp packets."}
[RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "dropped rtp packets."}
};
/* Aggregated RTP connection stats. These are updated when an RTP connection is freed.
@@ -181,55 +171,9 @@ static const struct rate_ctr_desc all_rtp_conn_rate_ctr_desc[] = {
[RTP_NUM_CONNECTIONS] = {"all_rtp:num_closed_conns", "Total number of rtp connections closed."}
};
/* Osmux connection related counters */
enum {
OSMUX_CHUNKS_RX_CTR,
OSMUX_OCTETS_RX_CTR,
OSMUX_RTP_PACKETS_TX_CTR,
OSMUX_RTP_PACKETS_TX_DROPPED_CTR,
OSMUX_AMR_OCTETS_TX_CTR,
/* Only available in global stats: */
OSMUX_NUM_CONNECTIONS,
OSMUX_PACKETS_RX_CTR,
OSMUX_PACKETS_TX_CTR,
OSMUX_DROPPED_PACKETS_CTR,
};
/* RTP per-connection statistics. Instances of the corresponding rate counter group
* exist for the lifetime of an RTP connection.
* Must be kept in sync with all_rtp_conn_rate_ctr_desc below */
static const struct rate_ctr_desc mgcp_conn_osmux_rate_ctr_desc[] = {
[OSMUX_CHUNKS_RX_CTR] = {"osmux:chunks_rx", "Inbound Osmux chunks."},
[OSMUX_OCTETS_RX_CTR] = {"osmux:octets_rx", "Inbound Osmux octets."},
[OSMUX_RTP_PACKETS_TX_CTR] = {"osmux:rtp_packets_tx", "Tx outbound RTP packets to encode as Osmux."},
[OSMUX_RTP_PACKETS_TX_DROPPED_CTR] = {"osmux:rtp_packets_tx_dropped", "Dropped Tx outbound RTP packets to encode as Osmux."},
[OSMUX_AMR_OCTETS_TX_CTR] = {"osmux:amr_octets_tx", "Tx outbound AMD payload octets."},
};
/* Aggregated Osmux connection stats. These are updated when an Osmux connection is freed.
* Must be kept in sync with mgcp_conn_osmux_rate_ctr_desc above */
static const struct rate_ctr_desc all_osmux_conn_rate_ctr_desc[] = {
[OSMUX_CHUNKS_RX_CTR] = {"all_osmux:chunks_rx", "Inbound Osmux chunks."},
[OSMUX_OCTETS_RX_CTR] = {"all_osmux:octets_rx", "Inbound Osmux octets."},
[OSMUX_RTP_PACKETS_TX_CTR] = {"all_osmux:rtp_packets_tx", "Tx outbound RTP packets to encode as Osmux."},
[OSMUX_RTP_PACKETS_TX_DROPPED_CTR] = {"all_osmux:rtp_packets_tx_dropped", "Dropped Tx outbound RTP packets to encode as Osmux."},
[OSMUX_AMR_OCTETS_TX_CTR] = {"all_osmux:amr_octets_tx", "Tx outbound AMD payload octets."},
/* These last counters below do not exist in per-connection stats, only here: */
[OSMUX_NUM_CONNECTIONS] = {"all_osmux:num_closed_conns", "Total number of osmux connections closed."},
[OSMUX_PACKETS_RX_CTR] = {"all_osmux:packets_rx", "Total inbound UDP/Osmux packets."},
[OSMUX_PACKETS_TX_CTR] = {"all_osmux:packets_tx", "Total outbound UDP/Osmux packets."},
[OSMUX_DROPPED_PACKETS_CTR] = {"all_osmux:dropped_packets", "Dropped outbound UDP/Osmux packets."}
};
/* Was conn configured to handle Osmux? */
static inline bool mgcp_conn_rtp_is_osmux(const struct mgcp_conn_rtp *conn) {
return conn->type == MGCP_RTP_OSMUX;
}
/* Was conn configured to handle Osmux? */
static inline bool mgcp_conn_rtp_is_iuup(const struct mgcp_conn_rtp *conn)
{
return conn->type == MGCP_RTP_IUUP;
return conn->type == MGCP_OSMUX_BSC || conn->type == MGCP_OSMUX_BSC_NAT;
}
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,

View File

@@ -1,11 +1,7 @@
/* IuUP connection functionalitites */
/*
* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* (C) 2020 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* Author: Pau Espin Pedrol
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
@@ -20,14 +16,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <osmocom/core/msgb.h>
struct ctrl_handle *mgw_ctrl_interface_setup(struct mgcp_config *cfg,
const char *bind_addr, uint16_t port);
struct mgcp_conn_rtp;
int mgcp_conn_iuup_init(struct mgcp_conn_rtp *conn_rtp);
void mgcp_conn_iuup_cleanup(struct mgcp_conn_rtp *conn_rtp);
int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg);
int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn_rtp *conn_dest_rtp, struct msgb *msg);
int mgcp_conn_iuup_send_dummy(struct mgcp_conn_rtp *conn_rtp);

View File

@@ -40,13 +40,8 @@ LOGP(cat, level, "endpoint:%s " fmt, \
endp ? endp->name : "none", \
## args)
enum rtp_proto {
MGCP_PROTO_RTP,
MGCP_PROTO_RTCP,
};
struct osmo_rtp_msg_ctx {
enum rtp_proto proto;
int proto;
struct mgcp_conn_rtp *conn_src;
struct osmo_sockaddr *from_addr;
};

View File

@@ -9,8 +9,7 @@
/* The following constant defines an RTP dummy payload that is used for
* "UDP Hole Punching" (NAT) */
#define MGCP_DUMMY_LOAD 0x23
static const char rtp_dummy_payload[] = { MGCP_DUMMY_LOAD };
static const char rtp_dummy_payload[] = { 0x23 };
/* Check if the data in a given message buffer matches the rtp dummy payload
* defined above */
@@ -71,6 +70,8 @@ struct mgcp_rtp_state {
* data is just re-used) */
uint16_t alt_rtp_tx_sequence;
uint32_t alt_rtp_tx_ssrc;
bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
};
struct mgcp_rtp_codec {
@@ -93,7 +94,7 @@ struct mgcp_rtp_end {
struct osmo_sockaddr addr;
/* in network byte order */
int rtcp_port;
int rtp_port, rtcp_port;
/* currently selected audio codec */
struct mgcp_rtp_codec *codec;
@@ -109,8 +110,8 @@ struct mgcp_rtp_end {
uint32_t packet_duration_ms;
int maximum_packet_time; /* -1: not set */
char *fmtp_extra;
/* are we transmitting packets (true) or dropping (false) outbound packets */
bool output_enabled;
/* are we transmitting packets (1) or dropping (0) outbound packets */
int output_enabled;
/* FIXME: This parameter can be set + printed, but is nowhere used! */
int force_output_ptime;
@@ -157,7 +158,7 @@ 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);
void 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,
@@ -176,12 +177,3 @@ void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
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, const bool marker_bit);
void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
int id, int inc);
void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
int id);
void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg);
uint32_t mgcp_get_current_ts(unsigned codec_rate);
int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg, bool target_is_oa);

View File

@@ -80,8 +80,6 @@ struct mgcp_ratectr_trunk {
struct rate_ctr_group *mgcp_dlcx_ctr_group;
/* Rate counter group which aggregates stats of individual RTP connections. */
struct rate_ctr_group *all_rtp_conn_stats;
/* Rate counter group which aggregates stats of individual Osmux connections. */
struct rate_ctr_group *all_osmux_conn_stats;
/* Rate counter group which contains stats for E1 events (only valid for E1 trunks) */
struct rate_ctr_group *e1_stats;
};

View File

@@ -1,22 +1,25 @@
#pragma once
#include <stdint.h>
#include <osmocom/core/socket.h>
#include <osmocom/netif/osmux.h>
struct mgcp_conn_rtp;
struct mgcp_trunk;
struct mgcp_endpoint;
struct mgcp_conn_rtp;
int osmux_init(struct mgcp_trunk *trunk);
int osmux_init_conn(struct mgcp_conn_rtp *conn);
int conn_osmux_enable(struct mgcp_conn_rtp *conn);
#define OSMUX_PORT 1984
enum {
OSMUX_ROLE_BSC = 0,
OSMUX_ROLE_BSC_NAT,
};
int osmux_init(int role, struct mgcp_config *cfg);
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
struct osmo_sockaddr *addr, uint16_t port);
void conn_osmux_disable(struct mgcp_conn_rtp *conn);
int conn_osmux_event_rx_crcx_mdcx(struct mgcp_conn_rtp *conn);
int conn_osmux_send_rtp(struct mgcp_conn_rtp *conn, struct msgb *msg);
int osmux_send_dummy(struct mgcp_conn_rtp *conn);
int conn_osmux_allocate_cid(struct mgcp_conn_rtp *conn, int osmux_cid);
void conn_osmux_release_cid(struct mgcp_conn_rtp *conn);
int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn);
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
void osmux_cid_pool_get(uint8_t osmux_cid);
int osmux_cid_pool_get_next(void);
@@ -26,14 +29,10 @@ int osmux_cid_pool_count_used(void);
enum osmux_state {
OSMUX_STATE_DISABLED = 0, /* Osmux not being currently used by endp */
OSMUX_STATE_ACTIVATING, /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref conn_osmux_enable. */
OSMUX_STATE_ENABLED, /* Osmux was initialized by \ref conn_osmux_enable and can process frames */
OSMUX_STATE_ACTIVATING, /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref osmux_enable_endpoint. */
OSMUX_STATE_ENABLED, /* Osmux was initialized by \ref osmux_enable_endpoint and can process frames */
};
extern const struct value_string osmux_state_strs[];
static inline const char *osmux_state_str(enum osmux_state val)
{ return get_value_string(osmux_state_strs, val); }
enum osmux_usage {
OSMUX_USAGE_OFF = 0,
OSMUX_USAGE_ON = 1,

View File

@@ -7,7 +7,7 @@
/* See also: RFC 3435, chapter 3.5 Transmission over UDP */
#define MGCP_CLIENT_LOCAL_ADDR_DEFAULT NULL /* INADDR(6)_ANY */
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 0
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 2727
#define MGCP_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1"
#define MGCP_CLIENT_REMOTE_PORT_DEFAULT 2427
@@ -49,7 +49,6 @@ enum mgcp_codecs {
CODEC_GSMHR_8000_1 = 111,
CODEC_AMR_8000_1 = 112,
CODEC_AMRWB_16000_1 = 113,
CODEC_IUFP = 96,
};
/* Note: when new codec types are added, the corresponding value strings
* in mgcp_client.c (codec_table) must be updated as well. Enumerations
@@ -140,7 +139,7 @@ struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp);
struct mgcp_client *mgcp_client_init(void *ctx,
struct mgcp_client_conf *conf);
int mgcp_client_connect(struct mgcp_client *mgcp);
int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports) OSMO_DEPRECATED("Use mgcp_client_connect() instead");
int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports);
void mgcp_client_disconnect(struct mgcp_client *mgcp);
const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp);

View File

@@ -16,7 +16,7 @@ struct mgcp_client {
mgcp_trans_id_t next_trans_id;
struct llist_head responses_pending;
struct llist_head inuse_endpoints;
struct mgcp_client_pool_member *pool_member;
struct mgcp_client_pool *pool;
};
struct mgcp_inuse_endpoint {

View File

@@ -1,21 +1,11 @@
#pragma once
#include <stdbool.h>
struct mgcp_client;
struct mgcp_client_pool;
struct mgcp_client_pool_member;
struct mgcp_client_pool *mgcp_client_pool_alloc(void *talloc_ctx);
void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent, struct mgcp_client_pool *pool);
int mgcp_client_pool_config_write(struct vty *vty, const char *indent);
unsigned int mgcp_client_pool_connect(struct mgcp_client_pool *pool);
void mgcp_client_pool_register_single(struct mgcp_client_pool *pool, struct mgcp_client *mgcp_client);
struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool);
void mgcp_client_pool_put(struct mgcp_client *mgcp_client);
struct mgcp_client_pool_member *mgcp_client_pool_find_member_by_nr(struct mgcp_client_pool *pool, unsigned int nr);
struct mgcp_client *mgcp_client_pool_member_get(struct mgcp_client_pool_member *pool_member);
bool mgcp_client_pool_member_is_blocked(const struct mgcp_client_pool_member *pool_member);

View File

@@ -1,31 +1,9 @@
#pragma once
/* Struct to handle a pool of MGWs. (Use _pool functions) */
struct mgcp_client_pool {
/* A pointer to a 'single' mgcp client. This is a non-pooled MGCP client that is configured using
* mgcp_client_vty_init() and actively registered by the API user using mgcp_client_pool_register_single() */
struct mgcp_client *mgcp_client_single;
/* A list that manages the pool members (see mgcp_client_pool_member->list above) */
struct llist_head member_list;
/* String to use for indentation when writing the configuration file to the VTY. This field is populated by
* mgcp_client_pool_vty_init() */
char *vty_indent;
/* VTY node specification used with this pool. This field is populated by mgcp_client_pool_vty_init() */
struct cmd_node *vty_node;
};
/* Struct to handle a member of a pool of MGWs. */
struct mgcp_client_pool_member {
/* Entry in llist mgcp_client_pool->pool. */
struct llist_head list;
/* The pool managing this object: */
struct mgcp_client_pool *pool;
/* Reference number assinged by VTY. This number is used to manage the pool from the VTY and to identify it in
* the log. */
unsigned int nr;
@@ -46,7 +24,22 @@ struct mgcp_client_pool_member {
unsigned int refcount;
};
struct mgcp_client_pool_member *mgcp_client_pool_member_alloc(struct mgcp_client_pool *pool, unsigned int nr);
void mgcp_client_pool_member_free(struct mgcp_client_pool_member *pool_member);
int mgcp_client_pool_member_reinit_client(struct mgcp_client_pool_member *pool_member);
/* Struct to handle a pool of MGWs. (Use _pool functions) */
struct mgcp_client_pool {
/* A pointer to a 'single' mgcp client. This is a non-pooled MGCP client that is configured using
* mgcp_client_vty_init() and actively registered by the API user using mgcp_client_pool_register_single() */
struct mgcp_client *mgcp_client_single;
/* A list that manages the pool members (see above) */
struct llist_head pool;
/* String to use for indentation when writing the configuration file to the VTY. This field is populated by
* mgcp_client_pool_vty_init() */
char *vty_indent;
/* VTY node specification used with this pool. This field is populated by mgcp_client_pool_vty_init() */
struct cmd_node *vty_node;
};
const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member);

View File

@@ -0,0 +1,74 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
#
# DESCRIPTION
#
# Check whether the given FLAG works with the current language's compiler
# or gives an error. (Warnings, however, are ignored)
#
# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
# success/failure.
#
# If EXTRA-FLAGS is defined, it is added to the current language's default
# flags (e.g. CFLAGS) when the check is done. The check is thus made with
# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
# force the compiler to issue an error when a bad flag is given.
#
# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
#
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
#
# LICENSE
#
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU 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 General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 4
AC_DEFUN([AX_CHECK_COMPILE_FLAG],
[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
[AS_VAR_SET(CACHEVAR,[yes])],
[AS_VAR_SET(CACHEVAR,[no])])
_AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
AS_VAR_IF(CACHEVAR,yes,
[m4_default([$2], :)],
[m4_default([$3], :)])
AS_VAR_POPDEF([CACHEVAR])dnl
])dnl AX_CHECK_COMPILE_FLAGS

View File

@@ -21,7 +21,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=10:0:1
MGCP_CLIENT_LIBVERSION=9:0:0
lib_LTLIBRARIES = \
libosmo-mgcp-client.la \
@@ -35,8 +35,4 @@ libosmo_mgcp_client_la_SOURCES = \
mgcp_client_pool.c \
$(NULL)
libosmo_mgcp_client_la_LDFLAGS = \
$(AM_LDFLAGS) \
-version-info $(MGCP_CLIENT_LIBVERSION) \
-no-undefined \
$(NULL)
libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION)

View File

@@ -59,7 +59,6 @@ const struct value_string osmo_mgcpc_codec_names[] = {
{ CODEC_GSMHR_8000_1, "GSM-HR-08/8000/1" },
{ CODEC_AMR_8000_1, "AMR/8000/1" },
{ CODEC_AMRWB_16000_1, "AMR-WB/16000/1" },
{ CODEC_IUFP, "VND.3GPP.IUFP/16000" },
{ 0, NULL },
};
@@ -442,7 +441,7 @@ static int mgcp_parse_osmux_cid(const char *line)
osmux_cid, OSMUX_CID_MAX);
return -2;
}
LOGP(DLMGCP, LOGL_DEBUG, "MGW offered Osmux CID %u\n", osmux_cid);
LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
return osmux_cid;
}
@@ -726,6 +725,10 @@ static int mgcp_do_read(struct osmo_fd *fd)
msgb_free(msg);
return -1;
} else if (ret > 4096 - 128) {
LOGPMGW(mgcp, LOGL_ERROR, "Too much data: %s: %d\n", osmo_sock_get_name2(fd->fd), ret);
msgb_free(msg);
return -1;
}
msg->l2h = msgb_put(msg, ret);
@@ -769,7 +772,7 @@ struct mgcp_client *mgcp_client_init(void *ctx,
mgcp->actual.local_addr = conf->local_addr ? conf->local_addr :
MGCP_CLIENT_LOCAL_ADDR_DEFAULT;
mgcp->actual.local_port = conf->local_port > 0 ? (uint16_t)conf->local_port :
mgcp->actual.local_port = conf->local_port >= 0 ? (uint16_t)conf->local_port :
MGCP_CLIENT_LOCAL_PORT_DEFAULT;
mgcp->actual.remote_addr = conf->remote_addr ? conf->remote_addr :
@@ -799,6 +802,49 @@ struct mgcp_client *mgcp_client_init(void *ctx,
return mgcp;
}
static int init_socket(struct mgcp_client *mgcp, unsigned int retry_n_ports)
{
int rc;
struct osmo_wqueue *wq;
unsigned int i;
wq = &mgcp->wq;
for (i = 0; i < retry_n_ports + 1; i++) {
/* Initialize socket with the currently configured port number */
rc = osmo_sock_init2_ofd(&wq->bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr,
mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
if (rc > 0)
return rc;
/* If there is a different port than the default port configured then we assume that the user has
* chosen that port conciously and we will not try to resolve this by silently choosing a different
* port. */
if (mgcp->actual.local_port != MGCP_CLIENT_LOCAL_PORT_DEFAULT && i == 0)
return -EINVAL;
if (i == retry_n_ports) {
/* Last try failed */
LOGPMGW(mgcp, LOGL_NOTICE, "Failed to bind to %s:%d -- check configuration!\n",
mgcp->actual.local_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.local_port);
if (retry_n_ports == 0)
return -EINVAL;
} else {
/* Choose a new port number to try next */
LOGPMGW(mgcp, LOGL_NOTICE,
"Failed to bind to %s:%d, retrying with port %d -- check configuration!\n",
mgcp->actual.local_addr ? mgcp->actual.local_addr : "(any)", mgcp->actual.local_port,
mgcp->actual.local_port + 1);
mgcp->actual.local_port++;
}
}
LOGPMGW(mgcp, LOGL_FATAL, "Failed to find a port to bind on %u times -- check configuration!\n", i);
return -EINVAL;
}
/* Safely ignore the MGCP response to the DLCX sent via _mgcp_client_send_dlcx() */
static void _ignore_mgcp_response(struct mgcp_response *response, void *priv) { }
@@ -835,8 +881,9 @@ static const char *_mgcp_client_name_append_domain(const struct mgcp_client *mgc
/*! Initialize client connection (opens socket)
* \param[in,out] mgcp MGCP client descriptor.
* \param[in] retry_n_ports number of consecutive local ports that should be used to retry on failure.
* \returns 0 on success, -EINVAL on error. */
int mgcp_client_connect(struct mgcp_client *mgcp)
int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports)
{
struct osmo_wqueue *wq;
int rc;
@@ -855,9 +902,7 @@ int mgcp_client_connect(struct mgcp_client *mgcp)
osmo_fd_setup(&wq->bfd, -1, OSMO_FD_READ, osmo_wqueue_bfd_cb, mgcp, 0);
rc = osmo_sock_init2_ofd(&wq->bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, mgcp->actual.local_addr,
mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
rc = init_socket(mgcp, retry_n_ports);
if (rc < 0) {
LOGPMGW(mgcp, LOGL_FATAL,
"Failed to initialize socket %s:%u -> %s:%u for MGW: %s\n",
@@ -883,12 +928,12 @@ error_close_fd:
return rc;
}
/*! DEPRECATED: Initialize client connection (opens socket)
/*! Initialize client connection (opens socket)
* \param[in,out] mgcp MGCP client descriptor.
* \returns 0 on success, -EINVAL on error. */
int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports)
int mgcp_client_connect(struct mgcp_client *mgcp)
{
return mgcp_client_connect(mgcp);
return mgcp_client_connect2(mgcp, 99);
}
/*! Terminate client connection

View File

@@ -227,7 +227,7 @@ static struct value_string osmo_mgcpc_ep_fsm_event_names[33] = {};
static char osmo_mgcpc_ep_fsm_event_name_bufs[32][32] = {};
static void fill_event_names(void)
static void fill_event_names()
{
int i;
for (i = 0; i < (ARRAY_SIZE(osmo_mgcpc_ep_fsm_event_names) - 1); i++) {
@@ -243,7 +243,7 @@ static void fill_event_names(void)
}
}
static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init(void)
static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init()
{
OSMO_ASSERT(osmo_fsm_register(&osmo_mgcpc_ep_fsm) == 0);
fill_event_names();
@@ -997,7 +997,7 @@ static int osmo_mgcpc_ep_fsm_timer_cb(struct osmo_fsm_inst *fi)
return 0;
}
static void osmo_mgcpc_ep_fsm_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
void osmo_mgcpc_ep_fsm_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
int i;
struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);

View File

@@ -760,7 +760,7 @@ const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info)
return buf;
}
static __attribute__((constructor)) void osmo_mgcp_client_fsm_init(void)
static __attribute__((constructor)) void osmo_mgcp_client_fsm_init()
{
OSMO_ASSERT(osmo_fsm_register(&fsm_mgcp_client) == 0);
}

View File

@@ -18,7 +18,6 @@
*
*/
#include <asm-generic/errno.h>
#include <osmocom/mgcp_client/mgcp_client.h>
#include <osmocom/mgcp_client/mgcp_client_internal.h>
#include <osmocom/mgcp_client/mgcp_client_pool_internal.h>
@@ -28,6 +27,32 @@
#define LOGPPMGW(pool_member, level, fmt, args...) \
LOGP(DLMGCP, level, "MGW-pool(%s) " fmt, mgcp_client_pool_member_name(pool_member), ## args)
/* Get a human readable name for a given pool member. */
const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member)
{
const struct mgcp_client *mpcp_client;
struct mgcp_client mpcp_client_dummy;
static char name[512];
const char *description;
if (!pool_member)
return "(null)";
/* It is not guranteed that a pool_member has an MGCP client. The client may not yet be initialized or the
* initalization may have been failed. In this case we will generate a dummy MGCP client to work with. */
if (!pool_member->client) {
memcpy(&mpcp_client_dummy.actual, &pool_member->conf, sizeof(mpcp_client_dummy.actual));
mpcp_client = &mpcp_client_dummy;
} else {
mpcp_client = pool_member->client;
}
description = mgcp_client_name(mpcp_client);
snprintf(name, sizeof(name), "%d:%s", pool_member->nr, description);
return name;
}
/*! Allocate MGCP client pool. This is called once on startup and before the pool is used with
* mgcp_client_pool_vty_init(). Since the pool is linked with the VTY it must exist througout the entire runtime.
* \param[in] talloc_ctx talloc context. */
@@ -39,7 +64,7 @@ struct mgcp_client_pool *mgcp_client_pool_alloc(void *talloc_ctx)
if (!pool)
return NULL;
INIT_LLIST_HEAD(&pool->member_list);
INIT_LLIST_HEAD(&pool->pool);
return pool;
}
@@ -52,11 +77,29 @@ unsigned int mgcp_client_pool_connect(struct mgcp_client_pool *pool)
struct mgcp_client_pool_member *pool_member;
unsigned int pool_members_initialized = 0;
llist_for_each_entry(pool_member, &pool->member_list, list) {
llist_for_each_entry(pool_member, &pool->pool, list) {
/* Initialize client */
if (mgcp_client_pool_member_reinit_client(pool_member) == 0)
pool_members_initialized++;
pool_member->client = mgcp_client_init(pool_member, &pool_member->conf);
if (!pool_member->client) {
LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client initialization failed\n");
continue;
}
/* Set backpointer so that we can detect later that this MGCP client is managed
* by this pool. */
pool_member->client->pool = pool;
/* Connect client */
if (mgcp_client_connect2(pool_member->client, 0)) {
LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client connect failed at (%s:%u)\n",
pool_member->conf.remote_addr, pool_member->conf.remote_port);
talloc_free(pool_member->client);
pool_member->client = NULL;
continue;
}
pool_members_initialized++;
}
return pool_members_initialized;
@@ -75,37 +118,23 @@ void mgcp_client_pool_register_single(struct mgcp_client_pool *pool, struct mgcp
pool->mgcp_client_single = mgcp_client;
}
/*! Lookup the selected MGCP client config by its reference number */
struct mgcp_client_pool_member *mgcp_client_pool_find_member_by_nr(struct mgcp_client_pool *pool, unsigned int nr)
{
struct mgcp_client_pool_member *pool_member;
llist_for_each_entry(pool_member, &pool->member_list, list) {
if (pool_member->nr == nr)
return pool_member;
}
return NULL;
}
/* Not every pool member may have a functional MGCP client, we will run through the pool once until we meet a
* pool member that is suitable (has a client, is not blocked, has a low load). */
static struct mgcp_client_pool_member *mgcp_client_pool_pick(struct mgcp_client_pool *pool)
{
struct mgcp_client_pool_member *pool_member;
struct mgcp_client_pool_member *pool_member_picked = NULL;
unsigned int n_pool_members = 0;
unsigned int n_pool_members = llist_count(&pool->pool);
llist_for_each_entry(pool_member, &pool->member_list, list) {
n_pool_members++;
llist_for_each_entry(pool_member, &pool->pool, list) {
if (pool_member->blocked == false && pool_member->client) {
if (!pool_member_picked)
pool_member_picked = pool_member;
else if (pool_member_picked->refcount > pool_member->refcount)
pool_member_picked = pool_member;
} else {
LOGPPMGW(pool_member, LOGL_DEBUG, "%s -- MGW %u is unusable (blocked=%u, cli=%u)\n",
__func__, pool_member->nr, pool_member->blocked, !!pool_member->client);
LOGPPMGW(pool_member, LOGL_DEBUG, "MGW pool has %u members -- MGW %u is unusable\n", n_pool_members,
pool_member->nr);
}
}
@@ -134,24 +163,26 @@ struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool)
* by the application code. */
/* When the pool is empty, return a single MGCP client if it is registered. */
if (llist_empty(&pool->member_list) && pool->mgcp_client_single) {
if (llist_empty(&pool->pool) && pool->mgcp_client_single) {
LOGP(DLMGCP, LOGL_DEBUG, "MGW pool is empty -- using (single) MGW %s\n",
mgcp_client_name(pool->mgcp_client_single));
return pool->mgcp_client_single;
}
/* Abort when the pool is empty */
if (llist_empty(&pool->member_list)) {
if (llist_empty(&pool->pool)) {
LOGP(DLMGCP, LOGL_ERROR, "MGW pool is empty -- no MGW available!\n");
return NULL;
}
/* Pick a suitable pool member */
pool_member = mgcp_client_pool_pick(pool);
if (!pool_member)
return NULL;
if (pool_member) {
pool_member->refcount++;
return pool_member->client;
}
return mgcp_client_pool_member_get(pool_member);
return NULL;
}
/*! put an MGCP client back into the pool (decrement reference counter).
@@ -163,130 +194,23 @@ struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool)
void mgcp_client_pool_put(struct mgcp_client *mgcp_client)
{
struct mgcp_client_pool_member *pool_member;
struct mgcp_client_pool *pool;
if (!mgcp_client)
return;
if (!mgcp_client->pool_member)
if (mgcp_client->pool)
pool = mgcp_client->pool;
else
return;
pool_member = mgcp_client->pool_member;
if (pool_member->refcount == 0) {
LOGPPMGW(pool_member, LOGL_ERROR, "MGW pool member has invalid refcount\n");
return;
llist_for_each_entry(pool_member, &pool->pool, list) {
if (pool_member->client == mgcp_client) {
if (pool_member->refcount == 0) {
LOGPPMGW(pool_member, LOGL_ERROR, "MGW pool member has invalid refcount\n");
return;
}
pool_member->refcount--;
}
}
pool_member->refcount--;
}
/***************************
* mgcp_client_pool_member:
***************************/
/*! Allocate an mgcp_client_pool_member.
* \param[in] pool MGCP client pool descriptor.
* \param[in] nr Reference number of the pool member.
*/
struct mgcp_client_pool_member *mgcp_client_pool_member_alloc(struct mgcp_client_pool *pool, unsigned int nr)
{
struct mgcp_client_pool_member *pool_member;
pool_member = talloc_zero(pool, struct mgcp_client_pool_member);
OSMO_ASSERT(pool_member);
mgcp_client_conf_init(&pool_member->conf);
pool_member->pool = pool;
pool_member->nr = nr;
llist_add_tail(&pool_member->list, &pool->member_list);
return pool_member;
}
/*! Free an mgcp_client_pool_member allocated through mgcp_client_pool_member_alloc().
* \param[in] pool_member MGCP client pool descriptor.
*
* It also frees the associated MGCP client if present.
*/
void mgcp_client_pool_member_free(struct mgcp_client_pool_member *pool_member)
{
llist_del(&pool_member->list);
if (pool_member->client) {
mgcp_client_disconnect(pool_member->client);
talloc_free(pool_member->client);
}
talloc_free(pool_member);
}
/*! Recreate and reconnect the MGCP client associated to the pool descriptor.
* \param[in] pool_member MGCP client pool descriptor.
*/
int mgcp_client_pool_member_reinit_client(struct mgcp_client_pool_member *pool_member)
{
/* Get rid of a possibly existing old MGCP client instance first */
if (pool_member->client) {
mgcp_client_disconnect(pool_member->client);
talloc_free(pool_member->client);
}
/* Initialize client */
pool_member->client = mgcp_client_init(pool_member, &pool_member->conf);
if (!pool_member->client) {
LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client initialization failed\n");
return -EINVAL;
}
/* Set backpointer so that we can detect later that this MGCP client is managed by this pool. */
pool_member->client->pool_member = pool_member;
/* Connect client */
if (mgcp_client_connect(pool_member->client)) {
LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client connect failed at (%s:%u)\n",
pool_member->conf.remote_addr, pool_member->conf.remote_port);
talloc_free(pool_member->client);
pool_member->client = NULL;
return -ECONNABORTED;
}
return 0;
}
/* Get a human readable name for a given pool member. */
const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member)
{
const struct mgcp_client *mpcp_client;
struct mgcp_client mpcp_client_dummy;
static char name[512];
const char *description;
if (!pool_member)
return "(null)";
/* It is not guranteed that a pool_member has an MGCP client. The client may not yet be initialized or the
* initalization may have been failed. In this case we will generate a dummy MGCP client to work with. */
if (!pool_member->client) {
memcpy(&mpcp_client_dummy.actual, &pool_member->conf, sizeof(mpcp_client_dummy.actual));
mpcp_client = &mpcp_client_dummy;
} else {
mpcp_client = pool_member->client;
}
description = mgcp_client_name(mpcp_client);
snprintf(name, sizeof(name), "%d:%s", pool_member->nr, description);
return name;
}
/*! Get the MGCP client associated with the pool reference from the pool (increment reference counter).
* \param[in] pool_member MGCP client pool descriptor.
* \returns MGCP client descriptor, NULL if no member was not ready.
*/
struct mgcp_client *mgcp_client_pool_member_get(struct mgcp_client_pool_member *pool_member)
{
pool_member->refcount++;
return pool_member->client;
}
/*! Get whether the MGCP client associated with the pool reference is blocked by policy.
* \param[in] pool_member MGCP client pool descriptor.
* \returns true if blocked, false otherwise
*/
bool mgcp_client_pool_member_is_blocked(const struct mgcp_client_pool_member *pool_member)
{
return pool_member->blocked;
}

View File

@@ -32,11 +32,10 @@
#include <osmocom/mgcp_client/mgcp_client.h>
#include <osmocom/mgcp_client/mgcp_client_internal.h>
#include <osmocom/mgcp_client/mgcp_client_pool_internal.h>
#include <osmocom/mgcp_client/mgcp_client_pool.h>
#define MGW_STR MGCP_CLIENT_MGW_STR
/* Only common (non-pooled) VTY commands will use this talloc context. All
/* Only common (non-pooled) VTY connands will use this talloc context. All
* pooled VTY commands will use the pool (global_mgcp_client_pool) as
* talloc context. */
static void *global_mgcp_client_ctx = NULL;
@@ -52,18 +51,13 @@ struct mgcp_client_conf *get_mgcp_client_config(struct vty *vty)
{
if (global_mgcp_client_pool && vty->node == global_mgcp_client_pool->vty_node->node)
return vty->index;
/* Global single MGCP config, deprecated: */
vty_out(vty, "%% MGCP commands outside of 'mgw' nodes are deprecated. "
"You should consider reading User Manual and migrating to 'mgw' node.%s",
VTY_NEWLINE);
return global_mgcp_client_conf;
else
return global_mgcp_client_conf;
}
DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd,
"local-ip " VTY_IPV46_CMD,
"local bind to connect to MGW from\n"
"mgw local-ip " VTY_IPV46_CMD,
MGW_STR "local bind to connect to MGW from\n"
"local bind IPv4 address\n"
"local bind IPv6 address\n")
{
@@ -78,16 +72,10 @@ ALIAS_DEPRECATED(cfg_mgw_local_ip, cfg_mgcpgw_local_ip_cmd,
"mgcpgw local-ip A.B.C.D",
MGW_STR "local bind to connect to MGCP gateway with\n"
"local bind IP address\n")
ALIAS_DEPRECATED(cfg_mgw_local_ip,
cfg_mgw_mgw_local_ip_cmd,
"mgw local-ip " VTY_IPV46_CMD,
MGW_STR "local bind to connect to MGW from\n"
"local bind IPv4 address\n"
"local bind IPv6 address\n")
DEFUN(cfg_mgw_local_port, cfg_mgw_local_port_cmd,
"local-port <0-65535>",
"local port to connect to MGW from\n"
"mgw local-port <0-65535>",
MGW_STR "local port to connect to MGW from\n"
"local bind port\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
@@ -99,15 +87,10 @@ ALIAS_DEPRECATED(cfg_mgw_local_port, cfg_mgcpgw_local_port_cmd,
"mgcpgw local-port <0-65535>",
MGW_STR "local bind to connect to MGCP gateway with\n"
"local bind port\n")
ALIAS_DEPRECATED(cfg_mgw_local_port,
cfg_mgw_mgw_local_port_cmd,
"mgw local-port <0-65535>",
MGW_STR "local port to connect to MGW from\n"
"local bind port\n")
DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd,
"remote-ip " VTY_IPV46_CMD,
"remote IP address to reach the MGW at\n"
"mgw remote-ip " VTY_IPV46_CMD,
MGW_STR "remote IP address to reach the MGW at\n"
"remote IPv4 address\n"
"remote IPv6 address\n")
{
@@ -121,16 +104,10 @@ ALIAS_DEPRECATED(cfg_mgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
"mgcpgw remote-ip A.B.C.D",
MGW_STR "remote bind to connect to MGCP gateway with\n"
"remote bind IP address\n")
ALIAS_DEPRECATED(cfg_mgw_remote_ip,
cfg_mgw_mgw_remote_ip_cmd,
"mgw remote-ip " VTY_IPV46_CMD,
MGW_STR "remote IP address to reach the MGW at\n"
"remote IPv4 address\n"
"remote IPv6 address\n")
DEFUN(cfg_mgw_remote_port, cfg_mgw_remote_port_cmd,
"remote-port <0-65535>",
"remote port to reach the MGW at\n"
"mgw remote-port <0-65535>",
MGW_STR "remote port to reach the MGW at\n"
"remote port\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
@@ -142,13 +119,8 @@ ALIAS_DEPRECATED(cfg_mgw_remote_port, cfg_mgcpgw_remote_port_cmd,
"mgcpgw remote-port <0-65535>",
MGW_STR "remote bind to connect to MGCP gateway with\n"
"remote bind port\n")
ALIAS_DEPRECATED(cfg_mgw_remote_port,
cfg_mgw_mgw_remote_port_cmd,
"mgw remote-port <0-65535>",
MGW_STR "remote port to reach the MGW at\n"
"remote port\n")
DEFUN_DEPRECATED(cfg_mgw_mgw_endpoint_range, cfg_mgw_mgw_endpoint_range_cmd,
DEFUN_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
"mgw endpoint-range <1-65534> <1-65534>",
MGW_STR "DEPRECATED: the endpoint range cannot be defined by the client\n"
"-\n" "-\n")
@@ -158,7 +130,7 @@ DEFUN_DEPRECATED(cfg_mgw_mgw_endpoint_range, cfg_mgw_mgw_endpoint_range_cmd,
VTY_NEWLINE);
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
"mgcpgw endpoint-range <1-65534> <1-65534>",
MGW_STR "usable range of endpoint identifiers\n"
"set first useable endpoint identifier\n"
@@ -186,8 +158,8 @@ ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port,
DEFUN(cfg_mgw_endpoint_domain_name,
cfg_mgw_endpoint_domain_name_cmd,
"endpoint-domain NAME",
"Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
"mgw endpoint-domain NAME",
MGW_STR "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
"Domain name, should be alphanumeric.\n")
{
struct mgcp_client_conf *conf = get_mgcp_client_config(vty);
@@ -200,16 +172,11 @@ DEFUN(cfg_mgw_endpoint_domain_name,
}
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_endpoint_domain_name,
cfg_mgw_mgw_endpoint_domain_name_cmd,
"mgw endpoint-domain NAME",
MGW_STR "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
"Domain name, should be alphanumeric.\n")
DEFUN(cfg_mgw_reset_ep_name,
cfg_mgw_reset_ep_name_cmd,
"reset-endpoint NAME",
"Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list,"
"mgw reset-endpoint NAME",
MGW_STR "Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list,"
"e.g. 'rtpbridge/*'\n"
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
{
@@ -247,17 +214,11 @@ DEFUN(cfg_mgw_reset_ep_name,
return CMD_SUCCESS;
}
ALIAS_DEPRECATED(cfg_mgw_reset_ep_name,
cfg_mgw_mgw_reset_ep_name_cmd,
"mgw reset-endpoint NAME",
MGW_STR "Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list,"
"e.g. 'rtpbridge/*'\n"
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
DEFUN(cfg_mgw_no_reset_ep_name,
cfg_mgw_no_reset_ep_name_cmd,
"no reset-endpoint NAME",
NO_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n"
"no mgw reset-endpoint NAME",
NO_STR MGW_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n"
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
{
struct reset_ep *reset_ep;
@@ -274,11 +235,6 @@ DEFUN(cfg_mgw_no_reset_ep_name,
vty_out(vty, "%% no such endpoint name configured ('%s')%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
ALIAS_DEPRECATED(cfg_mgw_no_reset_ep_name,
cfg_mgw_mgw_no_reset_ep_name_cmd,
"no mgw reset-endpoint NAME",
NO_STR MGW_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n"
"Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n")
static int config_write(struct vty *vty, const char *indent, struct mgcp_client_conf *conf)
{
@@ -286,36 +242,33 @@ static int config_write(struct vty *vty, const char *indent, struct mgcp_client_
int port;
struct reset_ep *reset_ep;
/* If caller doesn't the MGW pool API (mgcp_client_pool_vty_init was never called),
* then the "mgw" cmd prefix must be added since the old node always contained it.
*/
const char *mgw_prefix = global_mgcp_client_pool ? "" : "mgw ";
if (conf->description) /* description never had "mgw" prefix even on old node: */
if (conf->description)
vty_out(vty, "%sdescription %s%s", indent, conf->description, VTY_NEWLINE);
addr = conf->local_addr;
if (addr)
vty_out(vty, "%s%slocal-ip %s%s", indent, mgw_prefix, addr, VTY_NEWLINE);
vty_out(vty, "%smgw local-ip %s%s", indent, addr,
VTY_NEWLINE);
port = conf->local_port;
if (port >= 0)
vty_out(vty, "%s%slocal-port %u%s", indent, mgw_prefix,
vty_out(vty, "%smgw local-port %u%s", indent,
(uint16_t)port, VTY_NEWLINE);
addr = conf->remote_addr;
if (addr)
vty_out(vty, "%s%sremote-ip %s%s", indent, mgw_prefix, addr, VTY_NEWLINE);
vty_out(vty, "%smgw remote-ip %s%s", indent, addr,
VTY_NEWLINE);
port = conf->remote_port;
if (port >= 0)
vty_out(vty, "%s%sremote-port %u%s", indent, mgw_prefix,
vty_out(vty, "%smgw remote-port %u%s", indent,
(uint16_t)port, VTY_NEWLINE);
if (conf->endpoint_domain_name[0])
vty_out(vty, "%s%sendpoint-domain %s%s", indent, mgw_prefix,
vty_out(vty, "%smgw endpoint-domain %s%s", indent,
conf->endpoint_domain_name, VTY_NEWLINE);
llist_for_each_entry(reset_ep, &conf->reset_epnames, list)
vty_out(vty, "%s%sreset-endpoint %s%s", indent, mgw_prefix, reset_ep->name, VTY_NEWLINE);
vty_out(vty, "%smgw reset-endpoint %s%s", indent, reset_ep->name, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -326,11 +279,6 @@ static int config_write(struct vty *vty, const char *indent, struct mgcp_client_
* \returns CMD_SUCCESS on success, CMD_WARNING on error */
int mgcp_client_config_write(struct vty *vty, const char *indent)
{
/* If caller supports MGW pool API (mgcp_client_pool_vty_init was
* called), then skip printing any config in this node and print it when
* the whole 'mgw' node is printed. */
if (global_mgcp_client_pool)
return CMD_SUCCESS;
return config_write(vty, indent, global_mgcp_client_conf);
}
@@ -338,15 +286,15 @@ static void vty_init_common(void *talloc_ctx, int node)
{
global_mgcp_client_ctx = talloc_ctx;
/* deprecated 'mgw' commands ('mgw' prepended as first arg) */
install_lib_element(node, &cfg_mgw_mgw_local_ip_cmd);
install_lib_element(node, &cfg_mgw_mgw_local_port_cmd);
install_lib_element(node, &cfg_mgw_mgw_remote_ip_cmd);
install_lib_element(node, &cfg_mgw_mgw_remote_port_cmd);
install_lib_element(node, &cfg_mgw_mgw_endpoint_range_cmd);
install_lib_element(node, &cfg_mgw_mgw_endpoint_domain_name_cmd);
install_lib_element(node, &cfg_mgw_mgw_reset_ep_name_cmd);
install_lib_element(node, &cfg_mgw_mgw_no_reset_ep_name_cmd);
install_lib_element(node, &cfg_mgw_local_ip_cmd);
install_lib_element(node, &cfg_mgw_local_port_cmd);
install_lib_element(node, &cfg_mgw_remote_ip_cmd);
install_lib_element(node, &cfg_mgw_remote_port_cmd);
install_lib_element(node, &cfg_mgw_endpoint_range_cmd);
install_lib_element(node, &cfg_mgw_rtp_bts_base_port_cmd);
install_lib_element(node, &cfg_mgw_endpoint_domain_name_cmd);
install_lib_element(node, &cfg_mgw_reset_ep_name_cmd);
install_lib_element(node, &cfg_mgw_no_reset_ep_name_cmd);
osmo_fsm_vty_add_cmds();
}
@@ -371,62 +319,38 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c
vty_init_common(talloc_ctx, node);
}
/* Mark whether user called mgcp_client_pool_config_write() and hence support new API */
static bool mgcp_client_pool_config_write_called = false;
static int _mgcp_client_pool_config_write(struct vty *vty, const char *indent)
static int config_write_pool(struct vty *vty)
{
struct mgcp_client_pool *pool = global_mgcp_client_pool;
struct mgcp_client_pool_member *pool_member;
unsigned int subindent_buf_len;
char *subindent;
unsigned int indent_buf_len = strlen(pool->vty_indent) + 1 + 1;
char *indent = talloc_zero_size(vty, indent_buf_len);
if (!indent)
indent = pool->vty_indent ? : "";
subindent_buf_len = strlen(indent) + 1 + 1;
subindent = talloc_zero_size(vty, subindent_buf_len);
snprintf(indent, indent_buf_len, "%s ", pool->vty_indent);
snprintf(subindent, subindent_buf_len, "%s ", indent);
llist_for_each_entry(pool_member, &pool->member_list, list) {
vty_out(vty, "%smgw %u%s", indent, pool_member->nr, VTY_NEWLINE);
config_write(vty, subindent, &pool_member->conf);
llist_for_each_entry(pool_member, &pool->pool, list) {
vty_out(vty, "%smgw %u%s", pool->vty_indent, pool_member->nr, VTY_NEWLINE);
config_write(vty, indent, &pool_member->conf);
}
/* MGW pool API is supported by user (global_mgcp_client_pool is set
* because mgcp_client_pool_vty_init was called). If single MGW was
* configured through old VTY and no mgw in the new MGW pool VTY is
* replacing it, then output the single MGW converted to the new MGW
* pool VTY. */
if (llist_empty(&pool->member_list) && pool->mgcp_client_single) {
vty_out(vty, "%smgw 0%s", indent, VTY_NEWLINE);
config_write(vty, subindent, global_mgcp_client_conf);
}
talloc_free(subindent);
talloc_free(indent);
return CMD_SUCCESS;
}
/* Deprecated, used for backward compatibility with older users which didn't call
* mgcp_client_pool_config_write(): */
static int config_write_pool(struct vty *vty)
/* Lookup the selected MGCP client config by its reference number */
static struct mgcp_client_pool_member *pool_member_by_nr(unsigned int nr)
{
if (mgcp_client_pool_config_write_called)
return CMD_SUCCESS;
struct mgcp_client_pool_member *pool_member = NULL;
struct mgcp_client_pool_member *pool_member_tmp;
return _mgcp_client_pool_config_write(vty, NULL);
}
llist_for_each_entry(pool_member_tmp, &global_mgcp_client_pool->pool, list) {
if (pool_member_tmp->nr == nr) {
pool_member = pool_member_tmp;
break;
}
}
/*! Write out MGCP client config to VTY.
* \param[in] vty VTY to which we should print.
* \param[in] indent string used for indentation (e.g. " ").
If NULL, indentation passed during mgcp_client_pool_vty_init() will be used.
* \returns CMD_SUCCESS on success, CMD_WARNING on error */
int mgcp_client_pool_config_write(struct vty *vty, const char *indent)
{
/* Tell internal node write function that the user supports calling proper API: */
mgcp_client_pool_config_write_called = true;
return _mgcp_client_pool_config_write(vty, indent);
return pool_member;
}
DEFUN_ATTR(cfg_mgw,
@@ -435,10 +359,13 @@ DEFUN_ATTR(cfg_mgw,
int nr = atoi(argv[0]);
struct mgcp_client_pool_member *pool_member;
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
pool_member = pool_member_by_nr(nr);
if (!pool_member) {
pool_member = mgcp_client_pool_member_alloc(global_mgcp_client_pool, nr);
pool_member = talloc_zero(global_mgcp_client_pool, struct mgcp_client_pool_member);
OSMO_ASSERT(pool_member);
mgcp_client_conf_init(&pool_member->conf);
pool_member->nr = nr;
llist_add_tail(&pool_member->list, &global_mgcp_client_pool->pool);
}
vty->index = &pool_member->conf;
@@ -455,7 +382,7 @@ DEFUN_ATTR(cfg_no_mgw,
int nr = atoi(argv[0]);
struct mgcp_client_pool_member *pool_member;
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
pool_member = pool_member_by_nr(nr);
if (!pool_member) {
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
@@ -468,7 +395,12 @@ DEFUN_ATTR(cfg_no_mgw,
return CMD_WARNING;
}
mgcp_client_pool_member_free(pool_member);
llist_del(&pool_member->list);
if (pool_member->client) {
mgcp_client_disconnect(pool_member->client);
talloc_free(pool_member->client);
}
talloc_free(pool_member);
return CMD_SUCCESS;
}
@@ -480,7 +412,7 @@ DEFUN_ATTR(mgw_reconnect, mgw_reconnect_cmd,
int nr = atoi(argv[0]);
struct mgcp_client_pool_member *pool_member = NULL;
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
pool_member = pool_member_by_nr(nr);
if (!pool_member) {
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
@@ -493,10 +425,32 @@ DEFUN_ATTR(mgw_reconnect, mgw_reconnect_cmd,
return CMD_WARNING;
}
if (mgcp_client_pool_member_reinit_client(pool_member) < 0) {
/* Get rid of a possibly existing old MGCP client instance first */
if (pool_member->client) {
mgcp_client_disconnect(pool_member->client);
talloc_free(pool_member->client);
}
/* Create a new MGCP client instance with the current config */
pool_member->client = mgcp_client_init(pool_member, &pool_member->conf);
if (!pool_member->client) {
LOGP(DLMGCP, LOGL_ERROR, "(manual) MGW %s initalization failed\n",
mgcp_client_pool_member_name(pool_member));
vty_out(vty, "%% MGCP client (MGW %s) initalization failed ('%s')%s",
mgcp_client_pool_member_name(pool_member), argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
/* Set backpointer so that we can detect later that this MGCP client is managed by this pool. */
pool_member->client->pool = global_mgcp_client_pool;
/* Connect client */
if (mgcp_client_connect(pool_member->client)) {
LOGP(DLMGCP, LOGL_ERROR, "(manual) MGW %s connect failed at (%s:%u)\n",
mgcp_client_pool_member_name(pool_member), pool_member->conf.remote_addr,
pool_member->conf.remote_port);
talloc_free(pool_member->client);
pool_member->client = NULL;
vty_out(vty, "%% MGCP client (MGW %s) initalization failed ('%s')%s",
mgcp_client_pool_member_name(pool_member), argv[0], VTY_NEWLINE);
return CMD_WARNING;
@@ -512,7 +466,7 @@ DEFUN_ATTR(mgw_block, mgw_block_cmd,
int nr = atoi(argv[0]);
struct mgcp_client_pool_member *pool_member = NULL;
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
pool_member = pool_member_by_nr(nr);
if (!pool_member) {
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
@@ -529,7 +483,7 @@ DEFUN_ATTR(mgw_unblock, mgw_unblock_cmd,
int nr = atoi(argv[0]);
struct mgcp_client_pool_member *pool_member = NULL;
pool_member = mgcp_client_pool_find_member_by_nr(global_mgcp_client_pool, nr);
pool_member = pool_member_by_nr(nr);
if (!pool_member) {
vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
@@ -539,20 +493,20 @@ DEFUN_ATTR(mgw_unblock, mgw_unblock_cmd,
return CMD_SUCCESS;
}
DEFUN(mgw_show, mgw_show_cmd, "show mgw-pool", SHOW_STR "Display information about the MGW-Pool\n")
DEFUN(mgw_show, mgw_snow_cmd, "show mgw-pool", SHOW_STR "Display information about the MGW-Pool\n")
{
vty_out(vty, "%% MGW-Pool:%s", VTY_NEWLINE);
struct mgcp_client_pool_member *pool_member;
if (llist_empty(&global_mgcp_client_pool->member_list) && global_mgcp_client_pool->mgcp_client_single) {
if (llist_empty(&global_mgcp_client_pool->pool) && global_mgcp_client_pool->mgcp_client_single) {
vty_out(vty, "%% (pool is empty, single MGCP client will be used)%s", VTY_NEWLINE);
return CMD_SUCCESS;
} else if (llist_empty(&global_mgcp_client_pool->member_list)) {
} else if (llist_empty(&global_mgcp_client_pool->pool)) {
vty_out(vty, "%% (pool is empty)%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
llist_for_each_entry(pool_member, &global_mgcp_client_pool->member_list, list) {
llist_for_each_entry(pool_member, &global_mgcp_client_pool->pool, list) {
vty_out(vty, "%% MGW %s%s", mgcp_client_pool_member_name(pool_member), VTY_NEWLINE);
vty_out(vty, "%% mgcp-client: %s%s", pool_member->client ? "connected" : "disconnected",
VTY_NEWLINE);
@@ -566,8 +520,7 @@ DEFUN(mgw_show, mgw_show_cmd, "show mgw-pool", SHOW_STR "Display information abo
* (called once at startup by the application process).
* \param[in] parent_node identifier of the parent node on which the mgw node appears.
* \param[in] mgw_node identifier that should be used with the newly installed MGW node.
* \param[in] indent indentation string to match the indentation in the VTY config.
If NULL, it must be passed explicitly each time mgcp_client_pool_config_write() is called.
* \param[in] indent indentation string to match the indentation in the VTY config
* \param[in] pool user provided memory to store the configured MGCP client (MGW) pool. */
void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent, struct mgcp_client_pool *pool)
{
@@ -575,12 +528,11 @@ void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent
OSMO_ASSERT(pool);
/* Never allow this function to be called twice on the same pool */
OSMO_ASSERT(!pool->vty_indent);
OSMO_ASSERT(!pool->vty_node);
if (indent) {
pool->vty_indent = talloc_strdup(pool, indent);
OSMO_ASSERT(pool->vty_indent);
}
pool->vty_indent = talloc_strdup(pool, indent);
OSMO_ASSERT(pool->vty_indent);
pool->vty_node = talloc_zero(pool, struct cmd_node);
OSMO_ASSERT(pool->vty_node);
pool->vty_node->node = mgw_node;
@@ -590,19 +542,8 @@ void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent
install_lib_element(parent_node, &cfg_mgw_cmd);
install_lib_element(parent_node, &cfg_no_mgw_cmd);
/* Note: config_write_pool is deprecated and user is expected to
* manually call mgcp_client_pool_config_write() when printing the VTY
* config */
install_node(pool->vty_node, config_write_pool);
vty_init_common(pool, mgw_node);
install_lib_element(mgw_node, &cfg_mgw_local_ip_cmd);
install_lib_element(mgw_node, &cfg_mgw_local_port_cmd);
install_lib_element(mgw_node, &cfg_mgw_remote_ip_cmd);
install_lib_element(mgw_node, &cfg_mgw_remote_port_cmd);
install_lib_element(mgw_node, &cfg_mgw_rtp_bts_base_port_cmd);
install_lib_element(mgw_node, &cfg_mgw_endpoint_domain_name_cmd);
install_lib_element(mgw_node, &cfg_mgw_reset_ep_name_cmd);
install_lib_element(mgw_node, &cfg_mgw_no_reset_ep_name_cmd);
install_element(mgw_node, &cfg_description_cmd);
@@ -610,7 +551,7 @@ void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent
install_lib_element(ENABLE_NODE, &mgw_block_cmd);
install_lib_element(ENABLE_NODE, &mgw_unblock_cmd);
install_lib_element_ve(&mgw_show_cmd);
install_lib_element_ve(&mgw_snow_cmd);
global_mgcp_client_pool = pool;
}

View File

@@ -45,7 +45,7 @@ libosmo_mgcp_a_SOURCES = \
mgcp_stat.c \
mgcp_endp.c \
mgcp_trunk.c \
mgcp_ctrl.c \
mgcp_ratectr.c \
mgcp_e1.c \
mgcp_iuup.c \
$(NULL)

View File

@@ -345,18 +345,6 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
return -EINVAL;
}
/* Check if the codec has a specific AMR mode (octet-aligned or bandwith-efficient) set. */
bool mgcp_codec_amr_align_mode_is_indicated(const struct mgcp_rtp_codec *codec)
{
if (codec->param_present == false)
return false;
if (!codec->param.amr_octet_aligned_present)
return false;
if (strcmp(codec->subtype_name, "AMR") != 0)
return false;
return true;
}
/* Return true if octet-aligned is set in the given codec. Default to octet-aligned=0, i.e. bandwidth-efficient mode.
* See RFC4867 "RTP Payload Format for AMR and AMR-WB" sections "8.1. AMR Media Type Registration" and "8.2. AMR-WB
* Media Type Registration":
@@ -429,7 +417,7 @@ int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp
if (!codec_src)
return -EINVAL;
/* Use the codec information from the source and try to find the
/* Use the codec infrmation from the source and try to find the
* equivalent of it on the destination side */
codecs_assigned = rtp_dst->codecs_assigned;
OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS);

View File

@@ -30,14 +30,12 @@
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_sdp.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp/mgcp_iuup.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/timer.h>
#include <ctype.h>
static const struct rate_ctr_group_desc rate_ctr_group_desc = {
const static struct rate_ctr_group_desc rate_ctr_group_desc = {
.group_name_prefix = "conn_rtp",
.group_description = "rtp connection statistics",
.class_id = 1,
@@ -95,36 +93,30 @@ static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *
static atomic_uint rate_ctr_index = 0;
conn_rtp->type = MGCP_RTP_DEFAULT;
/* Osmux specific defaults, only used if conn is later on Osmux-enabled: */
conn_rtp->osmux.state = OSMUX_STATE_DISABLED;
conn_rtp->osmux.local_cid_allocated = false;
conn_rtp->osmux.local_cid = 0;
conn_rtp->osmux.remote_cid_present = false;
conn_rtp->osmux.remote_cid = 0;
conn_rtp->osmux.cid_allocated = false;
conn_rtp->osmux.cid = 0;
/* backpointer to the generic part of the connection */
conn->u.rtp.conn = conn;
end->rtp.fd = -1;
end->rtcp.fd = -1;
memset(&end->addr, 0, sizeof(end->addr));
end->rtcp_port = 0;
end->rtp_port = end->rtcp_port = 0;
talloc_free(end->fmtp_extra);
end->fmtp_extra = NULL;
/* Set default values */
end->frames_per_packet = 0; /* unknown */
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
end->output_enabled = false;
end->output_enabled = 0;
end->maximum_packet_time = -1;
conn_rtp->ctrg = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index++);
if (!conn_rtp->ctrg)
conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index++);
if (!conn_rtp->rate_ctr_group)
return -1;
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);
conn_rtp->state.in_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->rate_ctr_group, IN_STREAM_ERR_TSTMP_CTR);
conn_rtp->state.out_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->rate_ctr_group, OUT_STREAM_ERR_TSTMP_CTR);
/* Make sure codec table is reset */
mgcp_codec_reset_all(conn_rtp);
@@ -137,10 +129,8 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
{
if (mgcp_conn_rtp_is_osmux(conn_rtp))
conn_osmux_disable(conn_rtp);
if (mgcp_conn_rtp_is_iuup(conn_rtp))
mgcp_conn_iuup_cleanup(conn_rtp);
mgcp_free_rtp_port(&conn_rtp->end);
rate_ctr_group_free(conn_rtp->ctrg);
rate_ctr_group_free(conn_rtp->rate_ctr_group);
mgcp_codec_reset_all(conn_rtp);
}
@@ -269,7 +259,7 @@ struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
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;
struct rate_ctr_group *conn_stats = conn_rtp->ctrg;
struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
if (all_stats == NULL || conn_stats == NULL)
return;
@@ -366,38 +356,14 @@ char *mgcp_conn_dump(struct mgcp_conn *conn)
switch (conn->type) {
case MGCP_CONN_TYPE_RTP:
switch (conn->u.rtp.type) {
case MGCP_RTP_DEFAULT:
/* Dump RTP connection */
snprintf(str, sizeof(str), "(%s/rtp, id:0x%s, ip:%s, "
"rtp:%u rtcp:%u)",
conn->name, conn->id,
osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf),
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa),
ntohs(conn->u.rtp.end.rtcp_port));
break;
case MGCP_RTP_OSMUX:
snprintf(str, sizeof(str), "(%s/osmux, id:0x%s, ip:%s, "
"port:%u CID:%u)",
conn->name, conn->id,
osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf),
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa),
conn->u.rtp.osmux.local_cid);
break;
case MGCP_RTP_IUUP:
snprintf(str, sizeof(str), "(%s/iuup, id:0x%s, ip:%s, "
"port:%u)",
conn->name, conn->id,
osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf),
osmo_sockaddr_port(&conn->u.rtp.end.addr.u.sa));
break;
default:
/* Should not happen, we should be able to dump
* every possible connection type. */
snprintf(str, sizeof(str), "(unknown conn_rtp connection type %u)",
conn->u.rtp.type);
break;
}
/* Dump RTP connection */
snprintf(str, sizeof(str), "(%s/rtp, id:0x%s, ip:%s, "
"rtp:%u rtcp:%u)",
conn->name,
conn->id,
osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf),
ntohs(conn->u.rtp.end.rtp_port),
ntohs(conn->u.rtp.end.rtcp_port));
break;
default:

View File

@@ -0,0 +1,36 @@
/*
* (C) 2020 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/ctrl/control_if.h>
#include <osmocom/mgcp/mgcp.h>
static int mgw_ctrl_node_lookup(void *data, vector vline, int *node_type,
void **node_data, int *i)
{
return 0;
}
struct ctrl_handle *mgw_ctrl_interface_setup(struct mgcp_config *cfg,
const char *bind_addr, uint16_t port)
{
return ctrl_interface_setup_dynip2(cfg, bind_addr, port, mgw_ctrl_node_lookup,
_LAST_CTRL_NODE);
}

View File

@@ -338,7 +338,6 @@ static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
trunk = mgcp_trunk_by_line_num(cfg, ts->line->num);
if (!trunk) {
LOGP(DE1, LOGL_ERROR, "E1-RX: unable to find a trunk for E1-line %u!\n", ts->line->num);
msgb_free(msg);
return;
}
@@ -360,9 +359,6 @@ static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
/* Trigger sending of pending E1 traffic */
e1_send(ts, trunk);
/* e1inp_rx_ts() does not free() msgb */
msgb_free(msg);
}
static int e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr)
@@ -496,7 +492,7 @@ static bool tf_type_is_amr(enum osmo_trau_frame_type ft)
}
}
/*! Equip E1 endpoint with I.460 mux resources.
/* !Equip E1 endpoint with I.460 mux resources.
* \param[in] endp endpoint to equip
* \param[in] ts E1 timeslot number.
* \param[in] ss E1 subslot number.

View File

@@ -1,753 +0,0 @@
/*
* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All rights not specifically granted under this license are reserved.
*
* Author: Pau Espin Pedrol
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*/
#include <stdint.h>
#include <osmocom/core/byteswap.h>
#include <osmocom/gsm/iuup.h>
#include <osmocom/netif/rtp.h>
#include <osmocom/netif/amr.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_iuup.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp/mgcp_network.h>
#include <osmocom/mgcp/debug.h>
#define MGW_IUUP_MSGB_SIZE 4096
static const struct osmo_iuup_rnl_config def_configure_req = {
.transparent = false,
.active = true,
.supported_versions_mask = 0x0003,
.num_rfci = 0,
.num_subflows = 0,
.IPTIs_present = false,
.t_init = { .t_ms = IUUP_TIMER_INIT_T_DEFAULT, .n_max = IUUP_TIMER_INIT_N_DEFAULT },
.t_ta = { .t_ms = IUUP_TIMER_TA_T_DEFAULT, .n_max = IUUP_TIMER_TA_N_DEFAULT },
.t_rc = { .t_ms = IUUP_TIMER_RC_T_DEFAULT, .n_max = IUUP_TIMER_RC_N_DEFAULT },
};
/* Find a destination connection. */
static struct mgcp_conn *_find_dst_conn(struct mgcp_conn *conn)
{
/* NOTE: This code path runs every time an RTP packet is received. The
* function mgcp_find_dst_conn() we use to determine the detination
* connection will iterate the connection list inside the endpoint.
* Since list iterations are quite costly, we will figure out the
* destination only once and use the optional private data pointer of
* the connection to cache the destination connection pointer. */
struct mgcp_conn *conn_dst;
if (!conn->priv) {
conn_dst = mgcp_find_dst_conn(conn);
conn->priv = conn_dst;
} else {
conn_dst = (struct mgcp_conn *)conn->priv;
}
return conn_dst;
}
/* Find RFCI containing all 0 sizes, -1 if not found. irp is an Initialization.ind prim */
static int _find_rfci_no_data(struct osmo_iuup_rnl_prim *irp)
{
int i;
uint8_t rfci_cnt = 0;
/* Find RFCI containing NO_DATA: */
for (i = 0; i < ARRAY_SIZE(irp->u.status.u.initialization.rfci); i++) {
struct osmo_iuup_rfci *rfci = &irp->u.status.u.initialization.rfci[i];
int j;
bool is_no_data;
if (!rfci->used)
continue;
rfci_cnt++;
is_no_data = true;
for (j = 0; j < irp->u.status.u.initialization.num_subflows; j++) {
if (rfci->subflow_sizes[j]) {
is_no_data = false;
break;
}
}
if (is_no_data)
return rfci->id;
/* early loop termination: */
if (rfci_cnt == irp->u.status.u.initialization.num_subflows)
break;
}
return -1;
}
/* Lookup RFCI to use for specific AMR codec type. -1 if none found */
static int8_t _conn_iuup_amr_ft_2_rfci(struct mgcp_conn_rtp *conn_rtp, uint8_t ft)
{
int8_t i;
uint8_t rfci_cnt = 0;
unsigned match_bytes = (unsigned)osmo_amr_bytes(ft);
struct osmo_iuup_rnl_prim *irp = conn_rtp->iuup.init_ind;
if (!irp) {
/* No IuUP Initialization has occured on the IuUP side yet. Return error and drop the RTP data, until
* the IuUP Initialization has configured the link. */
return -1;
}
/* TODO: cache this somehow */
for (i = 0; i < ARRAY_SIZE(irp->u.status.u.initialization.rfci); i++) {
struct osmo_iuup_rfci *rfci = &irp->u.status.u.initialization.rfci[i];
int j;
unsigned num_bits;
if (!rfci->used)
continue;
rfci_cnt++;
num_bits = 0;
for (j = 0; j < irp->u.status.u.initialization.num_subflows; j++)
num_bits += rfci->subflow_sizes[j];
if (match_bytes == (num_bits + 7)/8)
return rfci->id;
/* early loop termination: */
if (rfci_cnt == irp->u.status.u.initialization.num_subflows)
break;
}
return -1;
}
/* Helper function to configure IuUP layer FSM as Init-Passive, based on default config */
static int _conn_iuup_configure_as_passive(struct mgcp_conn_rtp *conn_rtp)
{
struct osmo_iuup_rnl_prim *irp;
int rc;
conn_rtp->iuup.active_init = false;
/* Tx CONFIG.req */
irp = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE);
irp->u.config = def_configure_req;
irp->u.config.active = conn_rtp->iuup.active_init;
if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp)) == 0)
conn_rtp->iuup.configured = true;
else
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed configuring IuUP layer\n");
return rc;
}
/* Helper function to configure IuUP layer FSM as Init-Active, based on received
* RNL Status-Init primitive from the sister IuUP connection we will bridge to. */
static int _conn_iuup_configure_as_active(struct mgcp_conn_rtp *conn_rtp, struct osmo_iuup_rnl_prim *init_ind)
{
struct osmo_iuup_rnl_prim *irp = init_ind;
struct osmo_iuup_rnl_prim *irp2;
struct msgb *msg;
bool prev_output_enabled;
int rc;
conn_rtp->iuup.active_init = true;
/* Find RFCI containing NO_DATA: */
conn_rtp->iuup.rfci_id_no_data = _find_rfci_no_data(init_ind);
/* Copy over the rfci_id_no_data, since we reuse the same subflow set: */
msg = msgb_copy_c(conn_rtp->conn, irp->oph.msg, "iuup-init-copy");
conn_rtp->iuup.init_ind = (struct osmo_iuup_rnl_prim *)msgb_data(msg);
conn_rtp->iuup.init_ind->oph.msg = msg;
/* Tx CONFIG.req */
irp2 = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE);
irp2->u.config.transparent = false;
irp2->u.config.active = conn_rtp->iuup.active_init;
irp2->u.config.data_pdu_type = irp->u.status.u.initialization.data_pdu_type;
irp2->u.config.supported_versions_mask = def_configure_req.supported_versions_mask;
irp2->u.config.num_rfci = irp->u.status.u.initialization.num_rfci;
irp2->u.config.num_subflows = irp->u.status.u.initialization.num_subflows;
irp2->u.config.IPTIs_present = irp->u.status.u.initialization.IPTIs_present;
memcpy(irp2->u.config.rfci, irp->u.status.u.initialization.rfci, sizeof(irp2->u.config.rfci));
irp2->u.config.t_init = def_configure_req.t_init;
irp2->u.config.t_ta = def_configure_req.t_ta;
irp2->u.config.t_rc = def_configure_req.t_rc;
/* We need to force allowance of RTP containing Init-ACK back: */
prev_output_enabled = conn_rtp->end.output_enabled;
conn_rtp->end.output_enabled = true;
if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp2)) == 0)
conn_rtp->iuup.configured = true;
else
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed configuring IuUP layer\n");
conn_rtp->end.output_enabled = prev_output_enabled;
return rc;
}
/* Helper function to push an RTP+IuUP pkt up to the IuUP layer FSM through the
* TNL primitive interface. */
static int _conn_iuup_rtp_pl_up(struct mgcp_conn_rtp *conn_rtp, struct msgb *msg)
{
/* Send RTP payload (IuUP) up the stack: */
struct osmo_iuup_tnl_prim *itp;
int rc;
msg->l2h = msgb_data(msg) + sizeof(struct rtp_hdr);
itp = osmo_iuup_tnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, MGW_IUUP_MSGB_SIZE);
itp->oph.msg->l2h = msgb_put(itp->oph.msg, msgb_l2len(msg));
memcpy(itp->oph.msg->l2h, msgb_l2(msg), msgb_l2len(msg));
if ((rc = osmo_iuup_tnl_prim_up(conn_rtp->iuup.iui, itp)) != 0) {
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed passing IuUP-Init to IuUP layer\n");
}
return rc;
}
static int check_rtp_iuup(const struct mgcp_conn_rtp *conn_rtp, struct msgb *msg)
{
size_t min_size = sizeof(struct rtp_hdr);
/* Check there's at least 2 bytes of RTP payload (IuUP header). This is
** mainly to avoid 0-byte payload copy cases */
if (msgb_length(msg) < sizeof(struct rtp_hdr) + 2) {
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "RTP-IuUP packet too short (%u < %zu)\n",
msgb_length(msg), min_size);
return -1;
}
return 0;
}
/* Bridge received IuUP packet in conn_rtp_src to conn_rtp_dst, an IuUP sister
* conn in the endpoint. The function takes ownsership of the irp */
static int bridge_iuup_to_iuup_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mgcp_conn_rtp *conn_rtp_dst, struct osmo_iuup_rnl_prim *irp)
{
int rc;
/* If we are not configured and we received bridged data, it means
* conn_rtp_src is already configured and INITed, and we can infer
* conn_rtp_src is Init-passive (RNC side), so conn_rtp_dst needs to be
* configured as INIT-active: */
if (!conn_rtp_dst->iuup.configured) {
OSMO_ASSERT(conn_rtp_src->iuup.init_ind);
rc = _conn_iuup_configure_as_active(conn_rtp_dst, conn_rtp_src->iuup.init_ind);
if (rc < 0) {
msgb_free(irp->oph.msg);
return rc;
}
}
/* We simply forward the msg, without freeing it: */
talloc_steal(conn_rtp_dst->conn, irp->oph.msg);
irp->oph.operation = PRIM_OP_REQUEST;
if ((rc = osmo_iuup_rnl_prim_down(conn_rtp_dst->iuup.iui, irp)) != 0)
LOG_CONN_RTP(conn_rtp_dst, LOGL_ERROR, "Failed Tx data down to IuUP layer\n");
return rc;
}
/* Bridge received IuUP packet in conn_rtp_src to conn_rtp_dst, an RTP (no IuUP)
* sister conn in the endpoint. The function takes ownsership of the irp */
static int bridge_iuup_to_rtp_peer(struct mgcp_conn_rtp *conn_rtp_src, struct mgcp_conn_rtp *conn_rtp_dst, struct osmo_iuup_rnl_prim *irp)
{
/* FIXME: We probably need transcoding here?! Or at least look up AMR modes and translate to related RFCI */
uint8_t frame_nr = irp->u.data.frame_nr;
uint8_t fqc = irp->u.data.fqc;
struct msgb *msg = irp->oph.msg;
ssize_t amr_length = 0;
int ft;
uint8_t *amr_data;
struct rtp_hdr *rtp_hdr;
struct amr_hdr *amr_hdr;
int rc;
ft = osmo_amr_bytes_to_ft(msgb_l3len(msg));
if (ft < 0) {
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR,
"Unknown AMR format for size %u\n", msgb_l3len(msg));
msgb_free(msg);
return ft;
}
msgb_pull_to_l3(msg);
if (mgcp_codec_amr_is_octet_aligned(conn_rtp_dst->end.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 */
amr_hdr->f = 0;
amr_hdr->q = !fqc;
amr_hdr->ft = ft & 0xff;
amr_hdr->pad1 = 0;
amr_hdr->pad2 = 0;
} else {
OSMO_ASSERT(msgb_tailroom(msg) >= 2);
msgb_put(msg, 2);
osmo_amr_iuup_to_bwe(msgb_data(msg), msgb_length(msg) - 2, msgb_length(msg) + 2);
/* fill bwe header */
amr_data = msgb_data(msg);
/* CMR no change | follow bit | ft (3 of 4 bits) */
amr_data[0] = 15 << 4 | (0 << 3) | (ft >> 1);
amr_data[1] |= ((ft & 0x1) << 7) | (((!fqc) & 0x1) << 6);
amr_length = (osmo_amr_bits(ft) + 10 + 7) / 8;
msgb_trim(msg, amr_length);
LOGP(DLMGCP, LOGL_DEBUG, "Convert IuUP -> AMR BE: ft %d, len %zd\n", ft, amr_length);
}
rtp_hdr = (struct rtp_hdr *) msgb_push(msg, sizeof(*rtp_hdr));
*rtp_hdr = (struct rtp_hdr){
.csrc_count = 0,
.extension = 0,
.padding = 0,
.version = 0,
.payload_type = conn_rtp_dst->end.codec->payload_type,
.marker = 0,
.sequence = frame_nr,
.timestamp = 0,
.ssrc = 0
};
rc = mgcp_send(conn_rtp_dst->conn->endp, true, NULL, msg, conn_rtp_src, conn_rtp_dst);
msgb_free(msg);
return rc;
}
/* Handle RNL Data primitive received from the IuUP layer FSM: Bridge it to the
* sister connection in the endpoint: */
static int _conn_iuup_rx_rnl_data(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp)
{
struct mgcp_conn *conn_dst;
struct mgcp_conn_rtp *conn_rtp_dst;
int rc;
conn_dst = _find_dst_conn(conn_rtp_src->conn);
/* There is no destination conn, stop here */
if (!conn_dst) {
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_DEBUG,
"no connection to forward an incoming IuUP payload to\n");
rc = -1;
goto free_ret;
}
/* The destination conn is not an RTP/IuUP connection */
if (conn_dst->type != MGCP_CONN_TYPE_RTP) {
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR,
"unable to find suitable destination conn\n");
rc = -1;
goto free_ret;
}
conn_rtp_dst = &conn_dst->u.rtp;
switch (conn_rtp_dst->type) {
case MGCP_RTP_IUUP:
return bridge_iuup_to_iuup_peer(conn_rtp_src, conn_rtp_dst, irp);
case MGCP_RTP_DEFAULT:
return bridge_iuup_to_rtp_peer(conn_rtp_src, conn_rtp_dst, irp);
case MGCP_RTP_OSMUX:
default:
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_ERROR,
"Forward of IuUP payload to RTP connection type %u not supported!\n",
conn_rtp_dst->type);
rc = 0;
}
free_ret:
msgb_free(irp->oph.msg);
return rc;
}
/* Handle RNL Status-Init primitive received from the IuUP layer FSM.
* Potentially configure sister conn as IuUP Init-Active: */
static int _conn_iuup_rx_rnl_status_init(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp)
{
struct mgcp_conn *conn_dst;
struct mgcp_conn_rtp *conn_rtp_dst;
int rc = 0;
struct msgb *msg;
if (conn_rtp_src->iuup.init_ind) {
/* We received more than one IuUP Initialization. It's probably
* a retransmission, so simply ignore it (lower layers take care
* of ACKing it). */
LOGPCONN(conn_rtp_src->conn, DRTP, LOGL_INFO,
"Ignoring potential IuUP Initialization retrans\n");
return 0;
}
msg = msgb_copy_c(conn_rtp_src->conn, irp->oph.msg, "iuup-init-copy");
conn_rtp_src->iuup.init_ind = (struct osmo_iuup_rnl_prim *)msgb_data(msg);
conn_rtp_src->iuup.init_ind->oph.msg = msg;
/* Find RFCI containing NO_DATA: */
conn_rtp_src->iuup.rfci_id_no_data = _find_rfci_no_data(irp);
conn_dst = _find_dst_conn(conn_rtp_src->conn);
/* If not yet there, peer will potentially be IuUP-Initialized later
* when we attempt to bridge audio towards it. See bridge_iuup_to_iuup_peer() */
if (!conn_dst)
return 0;
conn_rtp_dst = &conn_dst->u.rtp;
if (!mgcp_conn_rtp_is_iuup(conn_rtp_dst))
return 0; /* Nothing to do */
/* We received IuUP parameters on the peer (RNC), Init actively this conn (against CN): */
if (!conn_rtp_dst->iuup.configured)
rc = _conn_iuup_configure_as_active(conn_rtp_dst, irp);
return rc;
}
/* Handle RNL Status primitives received from the IuUP layer FSM: */
static int _conn_iuup_rx_rnl_status(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp)
{
int rc;
switch (irp->u.status.procedure) {
case IUUP_PROC_INIT:
rc = _conn_iuup_rx_rnl_status_init(conn_rtp_src, irp);
break;
case IUUP_PROC_RATE_CTRL:
case IUUP_PROC_TIME_ALIGN:
case IUUP_PROC_ERR_EVENT:
default:
LOG_CONN_RTP(conn_rtp_src, LOGL_ERROR,
"Received IuUP RNL STATUS procedure type %u not handled\n",
irp->u.status.procedure);
rc = 0;
}
return rc;
}
/* Received RNL primitive from the IuUP layer FSM containing IuUP Status or
* data. Continue pushing it up the stack, either IuUP Status or Data: */
static int _conn_iuup_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
{
struct mgcp_conn_rtp *conn_rtp_src = ctx;
struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
struct msgb *msg = oph->msg;
int rc;
switch (OSMO_PRIM_HDR(&irp->oph)) {
case OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION):
/* we pass ownsership of msg here: */
rc = _conn_iuup_rx_rnl_data(conn_rtp_src, irp);
break;
case OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION):
rc = _conn_iuup_rx_rnl_status(conn_rtp_src, irp);
msgb_free(msg);
break;
default:
msgb_free(msg);
OSMO_ASSERT(false);
}
return rc;
}
/*! Send |RTP+IuUP| data down the stack of the specified destination connection.
* \param[in] endp associated endpoint (for configuration, logging).
* \param[in] buf buffer that contains the |RTP+IuUP| data.
* \param[in] len length of the buffer that contains the |RTP+IuUP| data.
* \param[in] conn_src associated source connection.
* \param[in] conn_dst associated destination connection.
* \returns 0 on success, -1 on ERROR. */
static int mgcp_send_iuup(struct mgcp_endpoint *endp, struct msgb *msg,
struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst)
{
/*! When no destination connection is available (e.g. when only one
* connection in loopback mode exists), then the source connection
* shall be specified as destination connection */
struct mgcp_rtp_end *rtp_end;
struct mgcp_rtp_state *rtp_state;
char ipbuf[INET6_ADDRSTRLEN];
struct rtp_hdr *hdr = (struct rtp_hdr *)msgb_data(msg);
int buflen = msgb_length(msg);
char *dest_name;
int len;
OSMO_ASSERT(conn_src);
OSMO_ASSERT(conn_dst);
LOGPENDP(endp, DRTP, LOGL_DEBUG, "delivering IuUP packet...\n");
/* Note: In case of loopback configuration, both, the source and the
* destination will point to the same connection. */
rtp_end = &conn_dst->end;
rtp_state = &conn_src->state;
dest_name = conn_dst->conn->name;
/* Ensure we have an alternative SSRC in case we need it, see also
* gen_rtp_header() */
if (rtp_state->alt_rtp_tx_ssrc == 0)
rtp_state->alt_rtp_tx_ssrc = rand();
if (!rtp_end->output_enabled) {
rtpconn_rate_ctr_add(conn_dst, endp, RTP_DROPPED_PACKETS_CTR, 1);
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"output disabled, drop to %s %s "
"rtp_port:%u rtcp_port:%u\n",
dest_name,
osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port)
);
return 0;
}
/* Specs say, in IuUP, the RTP seqnum and timestamp should actually be
* 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->sequence = osmo_htons(rtp_state->alt_rtp_tx_sequence);
hdr->ssrc = rtp_state->alt_rtp_tx_ssrc;
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"process/send IuUP to %s %s rtp_port:%u rtcp_port:%u\n",
dest_name, osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port));
/* Forward a copy of the RTP data to a debug ip/port */
forward_data_tap(rtp_end->rtp.fd, &conn_src->tap_out,
msg);
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, (char *)hdr, buflen);
if (len <= 0)
return len;
rtpconn_rate_ctr_add(conn_dst, endp, RTP_PACKETS_TX_CTR, 1);
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
rtp_state->alt_rtp_tx_sequence++;
return len;
}
/* Received TNL primitive from IuUP layer FSM, transmit it further down to the
* socket towards destination peer. */
static int _conn_iuup_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
{
struct mgcp_conn_rtp *conn_rtp_dst = ctx;
struct mgcp_conn *conn_dst = conn_rtp_dst->conn;
struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph;
struct mgcp_conn *conn_src;
struct msgb *msg;
struct rtp_hdr *rtph;
OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST));
msg = oph->msg;
talloc_steal(conn_rtp_dst->conn, msg);
msgb_pull_to_l2(msg);
rtph = (struct rtp_hdr *)msgb_push(msg, sizeof(*rtph));
/* TODO: fill rtph properly: */
*rtph = (struct rtp_hdr){
.csrc_count = 0,
.extension = 0,
.padding = 0,
.version = 2,
.payload_type = conn_rtp_dst->end.codec->payload_type,
.marker = 0,
.sequence = 0,
.timestamp = 0,
.ssrc = 0
};
/* The destination of the destination conn is the source conn, right? */
conn_src = _find_dst_conn(conn_dst);
if (!conn_src) {
LOG_CONN_RTP(conn_rtp_dst, LOGL_NOTICE,
"Couldn't find source conn for IuUP dst conn\n");
/* If there's no sister connection we are either still
* initializing (so we want to send back Init (ACK)), or we are
* probably in loopback mode anyway, so use dst as src. */
conn_src = conn_dst;
}
return mgcp_send_iuup(conn_dst->endp, msg, &conn_src->u.rtp, conn_rtp_dst);
}
/* Used to upgrade a regular RTP connection (MGCP_RTP_DEFAULT) to become a IuUP
* connection (MGCP_RTP_IUUP) */
int mgcp_conn_iuup_init(struct mgcp_conn_rtp *conn_rtp)
{
conn_rtp->type = MGCP_RTP_IUUP;
conn_rtp->iuup.iui = osmo_iuup_instance_alloc(conn_rtp->conn, conn_rtp->conn->id);
OSMO_ASSERT(conn_rtp->iuup.iui);
osmo_iuup_instance_set_user_prim_cb(conn_rtp->iuup.iui, _conn_iuup_user_prim_cb, conn_rtp);
osmo_iuup_instance_set_transport_prim_cb(conn_rtp->iuup.iui, _conn_iuup_transport_prim_cb, conn_rtp);
conn_rtp->iuup.rfci_id_no_data = -1;
return 0;
}
/* Cleanup specific IuUP connection (MGCP_RTP_IUUP) state, allocated by mgcp_conn_iuup_init() */
void mgcp_conn_iuup_cleanup(struct mgcp_conn_rtp *conn_rtp)
{
osmo_iuup_instance_free(conn_rtp->iuup.iui);
conn_rtp->iuup.iui = NULL;
}
/* Received RTP+IuUP pkt from socket of conn_rtp_src, build a TNL primitive to
* push it further up the stack to the IuUP layer FSM to handle and/or bridge it */
int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg)
{
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
struct mgcp_conn_rtp *conn_rtp_src = mc->conn_src;
int rc = 0;
bool force_output_enabled = false;
bool prev_output_enabled;
struct osmo_sockaddr prev_rem_addr;
uint16_t prev_rem_rtp_port;
OSMO_ASSERT(mgcp_conn_rtp_is_iuup(conn_rtp_src));
if ((rc = check_rtp_iuup(conn_rtp_src, msg)) < 0)
goto free_ret;
if (!conn_rtp_src->iuup.configured) {
/* We received the first message without sending any, the peer is the active side (RNC). */
rc = _conn_iuup_configure_as_passive(conn_rtp_src);
if (rc < 0)
goto free_ret;
/* We need to force allowance of RTP containing Init-ACK back: */
prev_output_enabled = conn_rtp_src->end.output_enabled;
conn_rtp_src->end.output_enabled = true;
force_output_enabled = true;
/* Fill in the peer address so that we can send Init-ACK back: */
prev_rem_addr = conn_rtp_src->end.addr;
prev_rem_rtp_port = osmo_sockaddr_port(&conn_rtp_src->end.addr.u.sa);
conn_rtp_src->end.addr = *mc->from_addr;
}
rc = _conn_iuup_rtp_pl_up(conn_rtp_src, msg);
if (force_output_enabled) {
conn_rtp_src->end.output_enabled = prev_output_enabled;
conn_rtp_src->end.addr = prev_rem_addr;
osmo_sockaddr_set_port(&conn_rtp_src->end.addr.u.sa, prev_rem_rtp_port);
}
return rc;
free_ret:
msgb_free(msg);
return rc;
}
/* Build IuUP RNL Data primitive from msg containing an incoming RTP pkt from
* peer and send it down the IuUP layer towards the destination as IuUP/RTP: */
int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn_rtp *conn_dest_rtp, struct msgb *msg)
{
struct osmo_iuup_rnl_prim *irp;
struct rtp_hdr *rtph;
int rc = -1;
int iuup_length = 0;
int8_t rfci;
/* Tx RNL-DATA.req */
rtph = (struct rtp_hdr *)msgb_data(msg);
msgb_pull(msg, sizeof(*rtph));
/* FIXME: validate amr packets */
irp = osmo_iuup_rnl_prim_alloc(conn_dest_rtp->conn, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE);
irp->u.data.frame_nr = htons(rtph->sequence) % 16;
/* TODO: CMR handling & multiple frames handling */
if (strcmp(conn_src_rtp->end.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);
goto free_ret;
}
if (mgcp_codec_amr_is_octet_aligned(conn_src_rtp->end.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,
"Bridge RTP=>IuUP: too short for AMR OA hdr (%u)\n", msgb_length(msg));
goto free_ret;
}
if (!osmo_amr_ft_valid(amr_hdr->ft)) {
LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR OA ft=%u\n", amr_hdr->ft);
goto free_ret;
}
if ((rfci = _conn_iuup_amr_ft_2_rfci(conn_dest_rtp, amr_hdr->ft)) < 0) {
LOG_CONN_RTP(conn_dest_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: No RFCI found for AMR OA ft=%u\n", amr_hdr->ft);
goto free_ret;
}
irp->u.data.fqc = amr_hdr->q ? IUUP_FQC_FRAME_GOOD : IUUP_FQC_FRAME_BAD;
irp->u.data.rfci = rfci;
msgb_pull(msg, 2);
LOGP(DLMGCP, LOGL_DEBUG, "Convert AMR OA -> IuUP: ft %d -> rfci %d len %d\n",
amr_hdr->ft, rfci, msgb_length(msg));
} else {
uint8_t *amr_bwe_hdr = (uint8_t *) msgb_data(msg);
int8_t ft;
uint8_t q;
if (msgb_length(msg) < 2) {
LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE,
"Bridge RTP=>IuUP: too short for AMR BE hdr (%u)\n", msgb_length(msg));
goto free_ret;
}
ft = ((amr_bwe_hdr[0] & 0x07) << 1) | ((amr_bwe_hdr[1] & 0x80) >> 7);
if (!osmo_amr_ft_valid(ft)) {
LOG_CONN_RTP(conn_src_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: wrong AMR BE ft=%u\n", ft);
goto free_ret;
}
if ((rfci = _conn_iuup_amr_ft_2_rfci(conn_dest_rtp, ft)) < 0) {
LOG_CONN_RTP(conn_dest_rtp, LOGL_NOTICE, "Bridge RTP=>IuUP: No RFCI found for AMR BE ft=%u\n", ft);
goto free_ret;
}
q = amr_bwe_hdr[1] & 0x40;
irp->u.data.fqc = q ? IUUP_FQC_FRAME_GOOD : IUUP_FQC_FRAME_BAD;
irp->u.data.rfci = rfci;
rc = iuup_length = osmo_amr_bwe_to_iuup(msgb_data(msg), msgb_length(msg));
if (rc < 0) {
LOG_CONN_RTP(conn_dest_rtp, LOGL_ERROR, "Bridge RTP=>IuUP: Failed convert the RTP/AMR to IuUP payload\n");
return rc;
}
msgb_trim(msg, iuup_length);
LOGP(DLMGCP, LOGL_DEBUG, "Convert AMR BE -> IuUP: ft %d -> rfci %d len %d\n",
ft, rfci, msgb_length(msg));
}
irp->oph.msg->l3h = msgb_put(irp->oph.msg, msgb_length(msg));
memcpy(irp->oph.msg->l3h, msgb_data(msg), msgb_length(msg));
if ((rc = osmo_iuup_rnl_prim_down(conn_dest_rtp->iuup.iui, irp)) != 0)
LOG_CONN_RTP(conn_dest_rtp, LOGL_ERROR, "Bridge RTP=>IuUP: Failed Tx RTP payload down the IuUP layer\n");
return rc;
free_ret:
msgb_free(irp->oph.msg);
return -1;
}
/* Build IuUP RNL Data primitive from msg containing dummy content and send it
* down the IuUP layer towards the destination as IuUP/RTP: */
int mgcp_conn_iuup_send_dummy(struct mgcp_conn_rtp *conn_rtp)
{
struct osmo_iuup_rnl_prim *irp;
int rc;
if (conn_rtp->iuup.rfci_id_no_data == -1) {
LOG_CONN_RTP(conn_rtp, LOGL_NOTICE, "No RFCI NO_DATA found, unable to send dummy packet\n");
return -ENOTSUP;
}
irp = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE);
irp->u.data.frame_nr = 0;
irp->u.data.fqc = IUUP_FQC_FRAME_GOOD;
irp->u.data.rfci = conn_rtp->iuup.rfci_id_no_data;
irp->oph.msg->l3h = irp->oph.msg->tail;
if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp)) != 0) {
LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed Tx RTP dummy payload down the IuUP layer\n");
return -EINVAL;
}
return 0;
}

View File

@@ -110,7 +110,8 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
/* Special handling for RTP connections */
if (conn->type == MGCP_CONN_TYPE_RTP) {
conn->u.rtp.end.output_enabled = !!(conn->mode & MGCP_CONN_SEND_ONLY);
conn->u.rtp.end.output_enabled =
conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0;
}
LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn));
@@ -120,7 +121,7 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
/* Special handling für RTP connections */
if (conn->type == MGCP_CONN_TYPE_RTP) {
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %u\n",
LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %d\n",
conn->u.rtp.end.output_enabled);
}
@@ -202,7 +203,7 @@ int mgcp_parse_osmux_cid(const char *line)
osmux_cid, OSMUX_CID_MAX);
return -2;
}
LOGP(DLMGCP, LOGL_DEBUG, "MGCP client offered Osmux CID %u\n", osmux_cid);
LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
return osmux_cid;
}

View File

@@ -48,16 +48,21 @@
#include <osmocom/mgcp/debug.h>
#include <osmocom/codec/codec.h>
#include <osmocom/mgcp/mgcp_e1.h>
#include <osmocom/mgcp/mgcp_iuup.h>
#define RTP_SEQ_MOD (1 << 16)
#define RTP_MAX_DROPOUT 3000
#define RTP_MAX_MISORDER 100
void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
enum rtp_proto {
MGCP_PROTO_RTP,
MGCP_PROTO_RTCP,
};
static void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp,
int id, int inc)
{
struct rate_ctr_group *conn_stats = conn_rtp->ctrg;
struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
struct rate_ctr_group *mgw_stats = endp->trunk->ratectr.all_rtp_conn_stats;
/* add to both the per-connection and the global stats */
@@ -65,68 +70,49 @@ void rtpconn_rate_ctr_add(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *
rate_ctr_add(rate_ctr_group_get_ctr(mgw_stats, id), inc);
}
void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp, int id)
static void rtpconn_rate_ctr_inc(struct mgcp_conn_rtp *conn_rtp, struct mgcp_endpoint *endp, int id)
{
rtpconn_rate_ctr_add(conn_rtp, endp, id, 1);
}
static int rx_rtp(struct msgb *msg);
static bool addr_is_any(const struct osmo_sockaddr *osa)
{
if (osa->u.sa.sa_family == AF_INET6) {
struct in6_addr ip6_any = IN6ADDR_ANY_INIT;
return memcmp(&osa->u.sin6.sin6_addr,
&ip6_any, sizeof(ip6_any)) == 0;
} else {
return osa->u.sin.sin_addr.s_addr == 0;
}
}
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);
return rtp_end->rtp_port && !addr_is_any(&rtp_end->addr);
}
/*! 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.
* \ returns 0 on success, -1 if no local address could be provided.
*
* The local bind IP-address is automatically selected by probing the
* IP-Address of the interface that is pointing towards the remote IP-Address,
* if no remote IP-Address is known yet, the statically configured
* IP-Addresses are used as fallback. */
int mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
{
const struct mgcp_endpoint *endp = conn->conn->endp;
const struct mgcp_config *cfg = endp->trunk->cfg;
struct mgcp_endpoint *endp;
char ipbuf[INET6_ADDRSTRLEN];
int rc;
bool rem_addr_set = osmo_sockaddr_is_any(&conn->end.addr) == 0;
const char *bind_addr;
/* Osmux: No smart IP addresses allocation is supported yet. Simply
* return the one set in VTY config: */
if (mgcp_conn_rtp_is_osmux(conn)) {
if (rem_addr_set) {
/* Match IP version with what was requested from remote: */
bind_addr = conn->end.addr.u.sa.sa_family == AF_INET6 ?
cfg->osmux.local_addr_v6 :
cfg->osmux.local_addr_v4;
} else {
/* Choose any of the bind addresses, preferring v6 over v4 if available: */
bind_addr = cfg->osmux.local_addr_v6;
if (!bind_addr)
bind_addr = cfg->osmux.local_addr_v4;
}
if (!bind_addr) {
LOGPCONN(conn->conn, DOSMUX, LOGL_ERROR,
"Unable to locate local Osmux address, check your configuration! v4=%u v6=%u remote_known=%s\n",
!!cfg->osmux.local_addr_v4,
!!cfg->osmux.local_addr_v6,
rem_addr_set ? osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf) : "no");
return -1;
}
LOGPCONN(conn->conn, DOSMUX, LOGL_DEBUG,
"Using configured osmux bind ip as local bind ip %s\n",
bind_addr);
osmo_strlcpy(addr, bind_addr, INET6_ADDRSTRLEN);
return 0;
}
endp = conn->conn->endp;
bool rem_addr_set = !addr_is_any(&conn->end.addr);
char *bind_addr;
/* Try probing the local IP-Address */
if (cfg->net_ports.bind_addr_probe && rem_addr_set) {
if (endp->trunk->cfg->net_ports.bind_addr_probe && rem_addr_set) {
rc = osmo_sock_local_ip(addr, osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf));
if (rc < 0)
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
@@ -135,22 +121,22 @@ int mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
"selected local rtp bind ip %s by probing using remote ip %s\n",
addr, osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf));
return 0;
return;
}
}
/* Select from preconfigured IP-Addresses. */
/* Select from preconfigured IP-Addresses. We don't have bind_addr for Osmux (yet?). */
if (rem_addr_set) {
/* Check there is a bind IP for the RTP traffic configured,
* if so, use that IP-Address */
bind_addr = conn->end.addr.u.sa.sa_family == AF_INET6 ?
cfg->net_ports.bind_addr_v6 :
cfg->net_ports.bind_addr_v4;
endp->trunk->cfg->net_ports.bind_addr_v6 :
endp->trunk->cfg->net_ports.bind_addr_v4;
} else {
/* Choose any of the bind addresses, preferring v6 over v4 */
bind_addr = cfg->net_ports.bind_addr_v6;
bind_addr = endp->trunk->cfg->net_ports.bind_addr_v6;
if (!strlen(bind_addr))
bind_addr = cfg->net_ports.bind_addr_v4;
bind_addr = endp->trunk->cfg->net_ports.bind_addr_v4;
}
if (strlen(bind_addr)) {
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
@@ -160,18 +146,17 @@ int mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn)
/* No specific bind IP is configured for the RTP traffic, so
* assume the IP where we listen for incoming MGCP messages
* as bind IP */
bind_addr = cfg->source_addr;
bind_addr = endp->trunk->cfg->source_addr;
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
"using mgcp bind ip as local rtp bind ip: %s\n", bind_addr);
}
osmo_strlcpy(addr, bind_addr, INET6_ADDRSTRLEN);
return 0;
}
/* This does not need to be a precision timestamp and
* is allowed to wrap quite fast. The returned value is
* 1/codec_rate seconds. */
uint32_t mgcp_get_current_ts(unsigned codec_rate)
static uint32_t get_current_ts(unsigned codec_rate)
{
struct timespec tp;
uint64_t ret;
@@ -544,7 +529,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 = get_current_ts(rtp_end->codec->rate);
ssrc = ntohl(rtp_hdr->ssrc);
marker_bit = !!rtp_hdr->marker;
transit = arrival_time - timestamp;
@@ -691,7 +676,7 @@ static int rfc5993_hr_convert(struct mgcp_endpoint *endp, struct msgb *msg)
{
struct rtp_hdr *rtp_hdr;
if (msgb_length(msg) < sizeof(struct rtp_hdr)) {
LOGPENDP(endp, DRTP, LOGL_ERROR, "RTP packet too short (%d < %zu)\n",
LOGPENDP(endp, DRTP, LOGL_ERROR, "AMR RTP packet too short (%d < %zu)\n",
msgb_length(msg), sizeof(struct rtp_hdr));
return -EINVAL;
}
@@ -717,18 +702,11 @@ static int rfc5993_hr_convert(struct mgcp_endpoint *endp, struct msgb *msg)
return 0;
}
/*! Convert msg to AMR RTP framing mode specified by target_is_oa.
* \param[in] endp MGCP Endpoint where this message belongs to (used for logging purposes)
* \param[in] msg Message containing an AMR RTP payload (in octet-aligned or bandwidth-efficient format).
* \param[in] target_is_oa the target framing mode that msg will contain after this function succeeds.
* \returns The size of the new RTP AMR content on success, negative on error.
*
* For AMR RTP two framing modes are defined RFC3267. There is a bandwidth
/* For AMR RTP two framing modes are defined RFC3267. There is a bandwith
* efficient encoding scheme where all fields are packed together one after
* another and an octet aligned mode where all fields are aligned to octet
* boundaries. This function is used to convert between the two modes.
*/
int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg,
* boundaries. This function is used to convert between the two modes */
static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg,
bool target_is_oa)
{
/* NOTE: the msgb has an allocated length of RTP_BUF_SIZE, so there is
@@ -737,7 +715,6 @@ int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg,
struct rtp_hdr *rtp_hdr;
unsigned int payload_len;
int rc;
bool orig_is_oa;
if (msgb_length(msg) < sizeof(struct rtp_hdr)) {
LOGPENDP(endp, DRTP, LOGL_ERROR, "AMR RTP packet too short (%d < %zu)\n", msgb_length(msg), sizeof(struct rtp_hdr));
@@ -745,10 +722,10 @@ int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg,
}
rtp_hdr = (struct rtp_hdr *)msgb_data(msg);
payload_len = msgb_length(msg) - sizeof(struct rtp_hdr);
orig_is_oa = osmo_amr_is_oa(rtp_hdr->data, payload_len);
if (orig_is_oa) {
payload_len = msgb_length(msg) - sizeof(struct rtp_hdr);
if (osmo_amr_is_oa(rtp_hdr->data, payload_len)) {
if (!target_is_oa)
/* Input data is oa an target format is bwe
* ==> convert */
@@ -770,16 +747,27 @@ int amr_oa_bwe_convert(struct mgcp_endpoint *endp, struct msgb *msg,
}
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR,
"RTP AMR packet conversion %s->%s failed: %s\n",
orig_is_oa ? "OA" : "BWE",
target_is_oa ? "OA" : "BWE",
osmo_hexdump(rtp_hdr->data, payload_len));
"AMR RTP packet conversion failed\n");
return -EINVAL;
}
return msgb_trim(msg, rc + sizeof(struct rtp_hdr));
}
/* Check if a conversion between octet-aligned and bandwith-efficient mode is
* indicated. */
static bool amr_oa_bwe_convert_indicated(struct mgcp_rtp_codec *codec)
{
if (codec->param_present == false)
return false;
if (!codec->param.amr_octet_aligned_present)
return false;
if (strcmp(codec->subtype_name, "AMR") != 0)
return false;
return true;
}
/* Return whether an RTP packet with AMR payload is in octet-aligned mode.
* Return 0 if in bandwidth-efficient mode, 1 for octet-aligned mode, and negative if the RTP data is invalid. */
static int amr_oa_check(char *data, int len)
@@ -801,7 +789,7 @@ static int amr_oa_check(char *data, int len)
/* Forward data to a debug tap. This is debug function that is intended for
* debugging the voice traffic with tools like gstreamer */
void forward_data_tap(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg)
static void forward_data(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg)
{
int rc;
@@ -827,7 +815,7 @@ static void gen_rtp_header(struct msgb *msg, struct mgcp_rtp_end *rtp_end,
hdr->version = 2;
hdr->payload_type = rtp_end->codec->payload_type;
hdr->timestamp = osmo_htonl(mgcp_get_current_ts(rtp_end->codec->rate));
hdr->timestamp = osmo_htonl(get_current_ts(rtp_end->codec->rate));
hdr->sequence = osmo_htons(state->alt_rtp_tx_sequence);
hdr->ssrc = state->alt_rtp_tx_ssrc;
}
@@ -838,7 +826,7 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, struct osmo_sockaddr *ad
{
char ipbuf[INET6_ADDRSTRLEN];
if (osmo_sockaddr_is_any(&conn->end.addr) != 0) {
if (addr_is_any(&conn->end.addr)) {
switch (conn->conn->mode) {
case MGCP_CONN_LOOPBACK:
/* HACK: for IuUP, we want to reply with an IuUP Initialization ACK upon the first RTP
@@ -887,14 +875,14 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, struct osmo_sockaddr *ad
* the same as the remote port where we transmit outgoing RTP traffic
* to (set by MDCX). We use this to check the origin of the data for
* plausibility. */
if (osmo_sockaddr_port(&conn->end.addr.u.sa) != osmo_sockaddr_port(&addr->u.sa) &&
if (ntohs(conn->end.rtp_port) != osmo_sockaddr_port(&addr->u.sa) &&
ntohs(conn->end.rtcp_port) != osmo_sockaddr_port(&addr->u.sa)) {
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
"data from wrong source port: %d, ",
osmo_sockaddr_port(&addr->u.sa));
LOGPC(DRTP, LOGL_ERROR,
"expected: %d for RTP or %d for RTCP\n",
osmo_sockaddr_port(&conn->end.addr.u.sa), ntohs(conn->end.rtcp_port));
ntohs(conn->end.rtp_port), ntohs(conn->end.rtcp_port));
LOGPCONN(conn->conn, DRTP, LOGL_ERROR, "packet tossed\n");
return -1;
}
@@ -907,29 +895,28 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, struct osmo_sockaddr *ad
static int check_rtp_destin(struct mgcp_conn_rtp *conn)
{
char ipbuf[INET6_ADDRSTRLEN];
bool ip_is_any = osmo_sockaddr_is_any(&conn->end.addr) != 0;
uint16_t port = osmo_sockaddr_port(&conn->end.addr.u.sa);
bool ip_is_any = addr_is_any(&conn->end.addr);
/* Note: it is legal to create a connection but never setting a port
* and IP-address for outgoing data. */
if (ip_is_any && port == 0) {
if (ip_is_any && conn->end.rtp_port == 0) {
LOGPCONN(conn->conn, DRTP, LOGL_DEBUG,
"destination IP-address and rtp port is not (yet) known (%s:%u)\n",
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), port);
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), conn->end.rtp_port);
return -1;
}
if (ip_is_any) {
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
"destination IP-address is invalid (%s:%u)\n",
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), port);
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), conn->end.rtp_port);
return -1;
}
if (port == 0) {
if (conn->end.rtp_port == 0) {
LOGPCONN(conn->conn, DRTP, LOGL_ERROR,
"destination rtp port is invalid (%s:%u)\n",
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), port);
osmo_sockaddr_ntop(&conn->end.addr.u.sa, ipbuf), conn->end.rtp_port);
return -1;
}
@@ -988,21 +975,14 @@ static int check_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
* the length is because we currently handle IUUP packets as RTP
* packets, so they must pass this check, if we weould be more
* strict here, we would possibly break 3G. (see also FIXME note
* below.*/
* below */
return 0;
}
/*! Dispatch msg bridged from the sister conn in the endpoint.
* \param[in] conn_dst The destination conn that should handle and transmit the content to
* its peer outside MGW.
* \param[in] msg msgb containing an RTP pkt received by the sister conn in the endpoint,
* \returns bytes sent, -1 on error.
*
* Possible options are standard RTP packet transmission, transmission
* via IuUP or transmission via an osmux connection.
*/
static int mgcp_conn_rtp_dispatch_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
/* Send RTP data. Possible options are standard RTP packet
* transmission or trsmission via an osmux connection */
static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
{
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
enum rtp_proto proto = mc->proto;
@@ -1027,24 +1007,12 @@ static int mgcp_conn_rtp_dispatch_rtp(struct mgcp_conn_rtp *conn_dst, struct msg
"using mgcp_send() to forward data directly\n");
return mgcp_send(endp, proto == MGCP_PROTO_RTP,
mc->from_addr, msg, conn_src, conn_dst);
case MGCP_RTP_OSMUX:
case MGCP_OSMUX_BSC_NAT:
case MGCP_OSMUX_BSC:
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"endpoint type is MGCP_RTP_OSMUX, "
"endpoint type is MGCP_OSMUX_BSC_NAT, "
"using osmux_xfrm_to_osmux() to forward data through OSMUX\n");
return conn_osmux_send_rtp(conn_dst, msg);
case MGCP_RTP_IUUP:
if (proto == MGCP_PROTO_RTP) {
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"endpoint type is MGCP_RTP_IUUP, "
"using mgcp_conn_iuup_send_rtp() to forward data over IuUP\n");
return mgcp_conn_iuup_send_rtp(conn_src, conn_dst, msg);
}
/* RTCP: we forward as usual for regular RTP connection */
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"endpoint type is MGCP_RTP_IUUP and proto!=MGCP_PROTO_RTP, "
"using mgcp_send() to forward data directly\n");
return mgcp_send(endp, false,
mc->from_addr, msg, conn_src, conn_dst);
return osmux_xfrm_to_osmux((char*)msgb_data(msg), msgb_length(msg), conn_dst);
}
/* If the data has not been handled/forwarded until here, it will
@@ -1058,22 +1026,26 @@ static int mgcp_conn_rtp_dispatch_rtp(struct mgcp_conn_rtp *conn_dst, struct msg
/*! send udp packet.
* \param[in] fd associated file descriptor.
* \param[in] addr destination ip-address.
* \param[in] port destination UDP port (network byte order).
* \param[in] buf buffer that holds the data to be send.
* \param[in] len length of the data to be sent.
* \returns bytes sent, -1 on error. */
int mgcp_udp_send(int fd, const struct osmo_sockaddr *addr, const char *buf, int len)
int mgcp_udp_send(int fd, struct osmo_sockaddr *addr, int port, const char *buf, int len)
{
char ipbuf[INET6_ADDRSTRLEN];
size_t addr_len;
bool is_ipv6 = addr->u.sa.sa_family == AF_INET6;
LOGP(DRTP, LOGL_DEBUG,
"sending %i bytes length packet to %s:%u ...\n", len,
osmo_sockaddr_ntop(&addr->u.sa, ipbuf),
osmo_sockaddr_port(&addr->u.sa));
ntohs(port));
if (addr->u.sa.sa_family == AF_INET6) {
if (is_ipv6) {
addr->u.sin6.sin6_port = port;
addr_len = sizeof(addr->u.sin6);
} else {
addr->u.sin.sin_port = port;
addr_len = sizeof(addr->u.sin);
}
@@ -1088,7 +1060,6 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
{
int rc;
int was_rtcp = 0;
struct osmo_sockaddr rtcp_addr;
OSMO_ASSERT(endp);
OSMO_ASSERT(conn);
@@ -1102,11 +1073,8 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
if (check_rtp_destin(conn) != 0)
goto failed;
if (mgcp_conn_rtp_is_iuup(conn))
rc = mgcp_conn_iuup_send_dummy(conn);
else
rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr,
rtp_dummy_payload, sizeof(rtp_dummy_payload));
rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr,
conn->end.rtp_port, rtp_dummy_payload, sizeof(rtp_dummy_payload));
if (rc == -1)
goto failed;
@@ -1115,10 +1083,8 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
return rc;
was_rtcp = 1;
rtcp_addr = conn->end.addr;
osmo_sockaddr_set_port(&rtcp_addr.u.sa, ntohs(conn->end.rtcp_port));
rc = mgcp_udp_send(conn->end.rtcp.fd, &rtcp_addr,
rtp_dummy_payload, sizeof(rtp_dummy_payload));
rc = mgcp_udp_send(conn->end.rtcp.fd, &conn->end.addr,
conn->end.rtcp_port, rtp_dummy_payload, sizeof(rtp_dummy_payload));
if (rc >= 0)
return rc;
@@ -1164,21 +1130,17 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
else
LOGPENDP(endp, DRTP, LOGL_DEBUG, "delivering RTCP packet...\n");
/* Patch the payload type number: translate from conn_src to conn_dst.
* Do not patch for IuUP, where the correct payload type number is already set in bridge_iuup_to_rtp_peer():
* IuUP -> AMR: calls this function, skip patching if conn_src is IuUP.
* {AMR or IuUP} -> IuUP: calls mgcp_udp_send() directly, skipping this function: No need to examine dst. */
if (is_rtp && !mgcp_conn_rtp_is_iuup(conn_src)) {
/* FIXME: It is legal that the payload type on the egress connection is
* different from the payload type that has been negotiated on the
* ingress connection. Essentially the codecs are the same so we can
* match them and patch the payload type. However, if we can not find
* the codec pendant (everything ist equal except the PT), we are of
* course unable to patch the payload type. A situation like this
* should not occur if transcoding is consequently avoided. Until
* we have transcoding support in osmo-mgw we can not resolve this. */
if (is_rtp) {
rc = mgcp_patch_pt(conn_src, conn_dst, msg);
if (rc < 0) {
/* FIXME: It is legal that the payload type on the egress connection is
* different from the payload type that has been negotiated on the
* ingress connection. Essentially the codecs are the same so we can
* match them and patch the payload type. However, if we can not find
* the codec pendant (everything ist equal except the PT), we are of
* course unable to patch the payload type. A situation like this
* should not occur if transcoding is consequently avoided. Until
* we have transcoding support in osmo-mgw we can not resolve this. */
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"can not patch PT because no suitable egress codec was found.\n");
}
@@ -1202,7 +1164,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
"rtp_port:%u rtcp_port:%u\n",
dest_name,
osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port)
ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)
);
} else if (is_rtp) {
int cont;
@@ -1223,15 +1185,12 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
mgcp_patch_and_count(endp, rtp_state, rtp_end,
addr, msg);
if (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)) {
if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) {
rc = amr_oa_bwe_convert(endp, msg,
conn_dst->end.codec->param.amr_octet_aligned);
if (rc < 0) {
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");
"Error in AMR octet-aligned <-> bandwidth-efficient mode conversion\n");
break;
}
} else if (rtp_end->rfc5993_hr_convert &&
@@ -1248,14 +1207,32 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
"rtp_port:%u rtcp_port:%u\n",
dest_name,
osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
osmo_sockaddr_port(&rtp_end->addr.u.sa), ntohs(rtp_end->rtcp_port)
ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)
);
/* Forward a copy of the RTP data to a debug ip/port */
forward_data_tap(rtp_end->rtp.fd, &conn_src->tap_out,
forward_data(rtp_end->rtp.fd, &conn_src->tap_out,
msg);
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr,
/* FIXME: HACK HACK HACK. See OS#2459.
* The ip.access nano3G needs the first RTP payload's first two bytes to read hex
* 'e400', or it will reject the RAB assignment. It seems to not harm other femto
* cells (as long as we patch only the first RTP payload in each stream).
*/
if (!rtp_state->patched_first_rtp_payload
&& conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
uint8_t *data = msgb_data(msg) + 12;
if (data[0] == 0xe0) {
data[0] = 0xe4;
data[1] = 0x00;
rtp_state->patched_first_rtp_payload = true;
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"Patching over first two bytes"
" to fake an IuUP Initialization Ack\n");
}
}
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port,
(char *)msgb_data(msg), msgb_length(msg));
if (len <= 0)
@@ -1270,17 +1247,15 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
} while (buflen > 0);
return nbytes;
} else if (!trunk->omit_rtcp) {
struct osmo_sockaddr rtcp_addr = rtp_end->addr;
osmo_sockaddr_set_port(&rtcp_addr.u.sa, rtp_end->rtcp_port);
LOGPENDP(endp, DRTP, LOGL_DEBUG,
"send to %s %s rtp_port:%u rtcp_port:%u\n",
dest_name, osmo_sockaddr_ntop(&rtcp_addr.u.sa, ipbuf),
osmo_sockaddr_port(&rtp_end->addr.u.sa),
osmo_sockaddr_port(&rtcp_addr.u.sa)
dest_name, osmo_sockaddr_ntop(&rtp_end->addr.u.sa, ipbuf),
ntohs(rtp_end->rtp_port), ntohs(rtp_end->rtcp_port)
);
len = mgcp_udp_send(rtp_end->rtcp.fd, &rtcp_addr,
(char *)msgb_data(msg), msgb_length(msg));
len = mgcp_udp_send(rtp_end->rtcp.fd,
&rtp_end->addr,
rtp_end->rtcp_port, (char *)msgb_data(msg), msgb_length(msg));
rtpconn_rate_ctr_inc(conn_dst, endp, RTP_PACKETS_TX_CTR);
rtpconn_rate_ctr_add(conn_dst, endp, RTP_OCTETS_TX_CTR, len);
@@ -1292,12 +1267,13 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr
return 0;
}
/*! Dispatch incoming RTP packet to opposite RTP connection.
* \param[in] msg Message buffer to bridge, coming from source connection.
* msg shall contain "struct osmo_rtp_msg_ctx *" attached in
* "OSMO_RTP_MSG_CTX(msg)".
* \returns 0 on success, -1 on ERROR.
*/
/*! dispatch incoming RTP packet to opposite RTP connection.
* \param[in] proto protocol (MGCP_CONN_TYPE_RTP or MGCP_CONN_TYPE_RTCP).
* \param[in] addr socket address where the RTP packet has been received from.
* \param[in] buf buffer that hold the RTP payload.
* \param[in] buf_size size data length of buf.
* \param[in] conn originating connection.
* \returns 0 on success, -1 on ERROR. */
int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
{
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
@@ -1315,9 +1291,6 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
* destination connection is known the RTP packet is sent via
* the destination connection. */
/* If source is IuUP, we need to handle state, forward it through specific bridge path: */
if (mgcp_conn_rtp_is_iuup(conn_src) && mc->proto == MGCP_PROTO_RTP)
return mgcp_conn_iuup_dispatch_rtp(msg);
/* Check if the connection is in loopback mode, if yes, just send the
* incoming data back to the origin */
@@ -1326,15 +1299,25 @@ 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) {
if (conn->u.rtp.end.rtp_port == 0) {
memcpy(&conn->u.rtp.end.addr, from_addr,
sizeof(conn->u.rtp.end.addr));
switch (from_addr->u.sa.sa_family) {
case AF_INET:
conn->u.rtp.end.rtp_port = from_addr->u.sin.sin_port;
break;
case AF_INET6:
conn->u.rtp.end.rtp_port = from_addr->u.sin6.sin6_port;
break;
default:
OSMO_ASSERT(false);
}
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));
conn->u.rtp.end.rtp_port);
}
return mgcp_conn_rtp_dispatch_rtp(conn_src, msg);
return mgcp_send_rtp(conn_src, msg);
}
/* Find a destination connection. */
@@ -1366,7 +1349,7 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
}
/* Dispatch RTP packet to destination RTP connection */
return mgcp_conn_rtp_dispatch_rtp(&conn_dst->u.rtp, msg);
return mgcp_send_rtp(&conn_dst->u.rtp, msg);
}
/*! dispatch incoming RTP packet to E1 subslot, handle RTCP packets locally.
@@ -1391,15 +1374,25 @@ 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) {
if (conn->u.rtp.end.rtp_port == 0) {
memcpy(&conn->u.rtp.end.addr, from_addr,
sizeof(conn->u.rtp.end.addr));
switch (from_addr->u.sa.sa_family) {
case AF_INET:
conn->u.rtp.end.rtp_port = from_addr->u.sin.sin_port;
break;
case AF_INET6:
conn->u.rtp.end.rtp_port = from_addr->u.sin6.sin6_port;
break;
default:
OSMO_ASSERT(false);
}
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));
conn->u.rtp.end.rtp_port);
}
return mgcp_conn_rtp_dispatch_rtp(conn_src, msg);
return mgcp_send_rtp(conn_src, msg);
}
/* Forward to E1 */
@@ -1475,7 +1468,7 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
msgb_put(msg, ret);
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s:%u\n",
proto == MGCP_PROTO_RTP ? "RTP" : "RTCP",
proto == MGCP_PROTO_RTP ? "RTP" : "RTPC",
msgb_length(msg), osmo_sockaddr_ntop(&addr.u.sa, ipbuf),
osmo_sockaddr_port(&addr.u.sa));
@@ -1508,12 +1501,12 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
sizeof(struct sockaddr_in)));
/* Increment RX statistics */
rate_ctr_inc(rate_ctr_group_get_ctr(conn_src->ctrg, RTP_PACKETS_RX_CTR));
rate_ctr_add(rate_ctr_group_get_ctr(conn_src->ctrg, RTP_OCTETS_RX_CTR), msgb_length(msg));
rate_ctr_inc(rate_ctr_group_get_ctr(conn_src->rate_ctr_group, RTP_PACKETS_RX_CTR));
rate_ctr_add(rate_ctr_group_get_ctr(conn_src->rate_ctr_group, RTP_OCTETS_RX_CTR), msgb_length(msg));
/* FIXME: count RTP and RTCP separately, also count IuUP payload-less separately */
/* Forward a copy of the RTP data to a debug ip/port */
forward_data_tap(fd->fd, &conn_src->tap_in, msg);
forward_data(fd->fd, &conn_src->tap_in, msg);
rc = rx_rtp(msg);
@@ -1522,7 +1515,6 @@ out:
return rc;
}
/* Note: This function is able to handle RTP and RTCP */
static int rx_rtp(struct msgb *msg)
{
struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg);
@@ -1533,29 +1525,23 @@ static int rx_rtp(struct msgb *msg)
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx_rtp(%u bytes)\n", msgb_length(msg));
/* Check if the origin of the RTP packet seems plausible */
if (!trunk->rtp_accept_all && check_rtp_origin(conn_src, from_addr))
return -1;
mgcp_conn_watchdog_kick(conn_src->conn);
/* If AMR is configured for the ingress connection and conversion of the
* framing mode (octet-aligned vs. bandwith-efficient) is explicitly
* defined, then we check if the incoming payload matches that
/* If AMR is configured for the ingress connection a conversion of the
* framing mode (octet-aligned vs. bandwith-efficient is explicitly
* define, then we check if the incoming payload matches that
* expectation. */
if (mc->proto == MGCP_PROTO_RTP &&
mgcp_codec_amr_align_mode_is_indicated(conn_src->end.codec)) {
if (amr_oa_bwe_convert_indicated(conn_src->end.codec)) {
int oa = amr_oa_check((char*)msgb_data(msg), msgb_length(msg));
if (oa < 0)
return -1;
if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned) {
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);
if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned)
return -1;
}
}
mgcp_conn_watchdog_kick(conn_src->conn);
/* Check if the origin of the RTP packet seems plausible */
if (!trunk->rtp_accept_all && check_rtp_origin(conn_src, from_addr))
return -1;
/* Execute endpoint specific implementation that handles the
* dispatching of the RTP data */

File diff suppressed because it is too large Load Diff

View File

@@ -46,8 +46,6 @@
#include <osmocom/mgcp/mgcp_sdp.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_iuup.h>
#include <osmocom/mgcp/debug.h>
/* Contains the last successfully resolved endpoint name. This variable is used
* for the unit-tests to verify that the endpoint was correctly resolved. */
@@ -149,12 +147,7 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp,
struct mgcp_conn_rtp *conn_dst = conn;
struct mgcp_conn *_conn;
switch (conn->type) {
case MGCP_RTP_DEFAULT:
case MGCP_RTP_OSMUX:
case MGCP_RTP_IUUP:
break;
default:
if (conn->type != MGCP_RTP_DEFAULT && !mgcp_conn_rtp_is_osmux(conn)) {
LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
"RTP-setup: Endpoint is not configured as RTP default, stopping here!\n");
return 0;
@@ -301,7 +294,7 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
/* Attach optional OSMUX parameters */
if (mgcp_conn_rtp_is_osmux(conn)) {
rc = msgb_printf(sdp, MGCP_X_OSMO_OSMUX_HEADER " %u\r\n", conn->osmux.local_cid);
rc = msgb_printf(sdp, "X-Osmux: %u\r\n", conn->osmux.cid);
if (rc < 0)
goto error;
}
@@ -324,13 +317,8 @@ error:
* osmux connection, send the dummy packet via OSMUX */
static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
{
/* Avoid sending dummy packet if the remote address was not yet
* configured through CRCX/MDCX: */
if (!mgcp_rtp_end_remote_addr_available(&conn->end))
return;
if (mgcp_conn_rtp_is_osmux(conn))
osmux_send_dummy(conn);
if (conn->osmux.state != OSMUX_STATE_DISABLED)
osmux_send_dummy(endp, conn);
else
mgcp_send_dummy(endp, conn);
}
@@ -714,7 +702,7 @@ void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
"Configuring RTP endpoint: local port %d%s%s\n",
osmo_sockaddr_port(&rtp->addr.u.sa),
ntohs(rtp->rtp_port),
rtp->force_aligned_timing ? ", force constant timing" : "",
rtp->force_constant_ssrc ? ", force constant ssrc" : "");
}
@@ -745,12 +733,12 @@ uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
*/
static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
{
if (!endp->trunk->cfg->osmux.initialized) {
if (osmux_init(endp->trunk) < 0) {
LOGPENDP(endp, DOSMUX, LOGL_ERROR, "Cannot init OSMUX\n");
if (!endp->trunk->cfg->osmux_init) {
if (osmux_init(OSMUX_ROLE_BSC, endp->trunk->cfg) < 0) {
LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
return -3;
}
LOGPENDP(endp, DOSMUX, LOGL_NOTICE, "OSMUX socket has been set up\n");
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
}
return mgcp_parse_osmux_cid(line);
@@ -851,7 +839,7 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
const char *callid = NULL;
const char *mode = NULL;
char *line;
int have_sdp = 0, remote_osmux_cid = -2;
int have_sdp = 0, osmux_cid = -2;
struct mgcp_conn_rtp *conn = NULL;
struct mgcp_conn *_conn = NULL;
char conn_name[512];
@@ -898,9 +886,9 @@ static struct msgb *handle_create_con(struct mgcp_request_data *rq)
case 'X':
if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
/* If osmux is disabled, just skip setting it up */
if (rq->endp->trunk->cfg->osmux.usage == OSMUX_USAGE_OFF)
if (!rq->endp->trunk->cfg->osmux)
break;
remote_osmux_cid = mgcp_osmux_setup(endp, line);
osmux_cid = mgcp_osmux_setup(endp, line);
break;
}
@@ -1003,17 +991,16 @@ mgcp_header_done:
goto error2;
}
/* If X-Osmux (remote CID) was received (-1 is wilcard), alloc next avail CID as local CID */
if (remote_osmux_cid >= -1) {
if (osmux_init_conn(conn) < 0) {
/* Annotate Osmux circuit ID and set it to negotiating state until this
* is fully set up from the dummy load. */
conn->osmux.state = OSMUX_STATE_DISABLED;
if (osmux_cid >= -1) { /* -1 is wilcard, alloc next avail CID */
conn->osmux.state = OSMUX_STATE_ACTIVATING;
if (conn_osmux_allocate_cid(conn, osmux_cid) == -1) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
goto error2;
}
if (remote_osmux_cid >= 0) {
conn->osmux.remote_cid_present = true;
conn->osmux.remote_cid = remote_osmux_cid;
}
} else if (endp->trunk->cfg->osmux.usage == OSMUX_USAGE_ONLY) {
} else if (endp->trunk->cfg->osmux == OSMUX_USAGE_ONLY) {
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
"CRCX: osmux only and no osmux offered\n");
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
@@ -1041,11 +1028,6 @@ mgcp_header_done:
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION));
goto error2;
}
/* Upgrade the conn type RTP_DEFAULT->RTP_IUUP if needed based on requested codec: */
/* TODO: "codec" probably needs to be moved from endp to conn */
if (conn->type == MGCP_RTP_DEFAULT && strcmp(conn->end.codec->subtype_name, "VND.3GPP.IUFP") == 0) {
rc = mgcp_conn_iuup_init(conn);
}
conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
trunk->audio_fmtp_extra);
@@ -1060,7 +1042,7 @@ mgcp_header_done:
/* check connection mode setting */
if (conn->conn->mode != MGCP_CONN_LOOPBACK
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
&& osmo_sockaddr_port(&conn->end.addr.u.sa) == 0) {
&& conn->end.rtp_port == 0) {
LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
"CRCX: selected connection mode type requires an opposite end!\n");
error_code = 527;
@@ -1070,10 +1052,7 @@ mgcp_header_done:
/* Find a local address for conn based on policy and initial SDP remote
information, then find a free port for it */
if (mgcp_get_local_addr(conn->end.local_addr, conn) < 0) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
goto error2;
}
mgcp_get_local_addr(conn->end.local_addr, conn);
if (allocate_port(endp, conn) != 0) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
goto error2;
@@ -1086,20 +1065,13 @@ mgcp_header_done:
goto error2;
}
/* Notify Osmux conn that CRCX was received */
if (mgcp_conn_rtp_is_osmux(conn)) {
if (conn_osmux_event_rx_crcx_mdcx(conn) < 0) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "CRCX: Osmux handling failed!\n");
goto error2;
}
}
LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
"CRCX: Creating connection: port: %u\n", conn->end.local_port);
/* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
mgcp_rtp_end_remote_addr_available(&conn->end) &&
trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
send_dummy(endp, conn);
@@ -1134,7 +1106,7 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
const char *mode = NULL;
struct mgcp_conn_rtp *conn = NULL;
const char *conn_id = NULL;
int remote_osmux_cid = -2;
int osmux_cid = -2;
int rc;
LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
@@ -1190,9 +1162,9 @@ static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
case 'X':
if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
/* If osmux is disabled, just skip setting it up */
if (endp->trunk->cfg->osmux.usage == OSMUX_USAGE_OFF)
if (!endp->trunk->cfg->osmux)
break;
remote_osmux_cid = mgcp_osmux_setup(endp, line);
osmux_cid = mgcp_osmux_setup(endp, line);
break;
}
/* Ignore unknown X-headers */
@@ -1269,39 +1241,30 @@ mgcp_header_done:
}
if (mgcp_conn_rtp_is_osmux(conn)) {
OSMO_ASSERT(conn->osmux.local_cid_allocated);
if (remote_osmux_cid < -1) {
OSMO_ASSERT(conn->osmux.cid_allocated);
if (osmux_cid < -1) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"MDCX: Failed to parse Osmux CID!\n");
goto error3;
} else if (remote_osmux_cid == -1) {
} else if (osmux_cid == -1) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"MDCX: wilcard in MDCX is not supported!\n");
goto error3;
} else if (conn->osmux.remote_cid_present &&
remote_osmux_cid != conn->osmux.remote_cid) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"MDCX: changing already allocated CID is not supported!\n");
goto error3;
} else {
conn->osmux.remote_cid_present = true;
conn->osmux.remote_cid = remote_osmux_cid;
if (conn_osmux_event_rx_crcx_mdcx(conn) < 0) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"MDCX: Osmux handling failed!\n");
goto error3;
}
} else if (osmux_cid != (int) conn->osmux.cid) {
LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
"MDCX: changing already allocated CID is not supported!\n");
goto error3;
}
/* TODO: In the future (when we have recvCID!=sendCID), we need to
tell Osmux code that osmux_cid is to be used as sendCID for
that conn. */
}
/* MDCX 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) < 0) {
rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
goto error3;
}
mgcp_get_local_addr(new_local_addr, conn);
if (strcmp(new_local_addr, conn->end.local_addr)) {
osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
mgcp_free_rtp_port(&conn->end);
@@ -1325,6 +1288,7 @@ mgcp_header_done:
/* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
mgcp_rtp_end_remote_addr_available(&conn->end) &&
trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
send_dummy(endp, conn);
@@ -1593,7 +1557,8 @@ static void mgcp_keepalive_timer_cb(void *_trunk)
struct mgcp_endpoint *endp = trunk->endpoints[i];
llist_for_each_entry(conn, &endp->conns, entry) {
if (conn->type == MGCP_CONN_TYPE_RTP &&
conn->mode == MGCP_CONN_RECV_ONLY)
conn->mode == MGCP_CONN_RECV_ONLY &&
mgcp_rtp_end_remote_addr_available(&conn->u.rtp.end))
send_dummy(endp, &conn->u.rtp);
}
}
@@ -1647,6 +1612,7 @@ struct mgcp_config *mgcp_config_alloc(void)
cfg->source_port = 2427;
osmo_strlcpy(cfg->source_addr, "0.0.0.0", sizeof(cfg->source_addr));
osmo_strlcpy(cfg->osmux_addr, "0.0.0.0", sizeof(cfg->osmux_addr));
cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;

View File

@@ -43,7 +43,7 @@ static const struct rate_ctr_desc mgcp_general_ctr_desc[] = {
{ "mgcp:err_rx_no_endpoint", "can't find MGCP endpoint, probably we've used all allocated endpoints." },
};
static const struct rate_ctr_group_desc mgcp_general_ctr_group_desc = {
const static struct rate_ctr_group_desc mgcp_general_ctr_group_desc = {
.group_name_prefix = "mgcp",
.group_description = "mgcp general statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
@@ -71,7 +71,7 @@ static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = {
[MGCP_CRCX_FAIL_CLAIM] = { "crcx:claim", "endpoint can not be claimed." },
};
static const struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
const static struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = {
.group_name_prefix = "crcx",
.group_description = "crxc statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
@@ -97,7 +97,7 @@ static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = {
[MGCP_MDCX_FAIL_AVAIL] = { "mdcx:unavailable", "endpoint unavailable." },
};
static const struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
const static struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = {
.group_name_prefix = "mdcx",
.group_description = "mdcx statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
@@ -116,7 +116,7 @@ static const struct rate_ctr_desc mgcp_dlcx_ctr_desc[] = {
[MGCP_DLCX_FAIL_AVAIL] = { "dlcx:unavailable", "endpoint unavailable." },
};
static const struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
const static struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = {
.group_name_prefix = "dlcx",
.group_description = "dlcx statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
@@ -130,7 +130,7 @@ static const struct rate_ctr_desc e1_rate_ctr_desc[] = {
[E1_I460_TRAU_MUX_EMPTY_CTR] = { "e1:i460", "Outbound I.460 MUX queue empty." }
};
static const struct rate_ctr_group_desc e1_rate_ctr_group_desc = {
const static struct rate_ctr_group_desc e1_rate_ctr_group_desc = {
.group_name_prefix = "e1",
.group_description = "e1 statistics",
.class_id = OSMO_STATS_CLASS_GLOBAL,
@@ -138,7 +138,7 @@ static const struct rate_ctr_group_desc e1_rate_ctr_group_desc = {
.ctr_desc = e1_rate_ctr_desc
};
static const struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
const static struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
.group_name_prefix = "all_rtp_conn",
.group_description = "aggregated statistics for all rtp connections",
.class_id = 1,
@@ -146,14 +146,6 @@ static const struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = {
.ctr_desc = all_rtp_conn_rate_ctr_desc
};
static const struct rate_ctr_group_desc all_osmux_conn_rate_ctr_group_desc = {
.group_name_prefix = "all_osmux_conn",
.group_description = "aggregated statistics for all osmux connections",
.class_id = 1,
.num_ctr = ARRAY_SIZE(all_osmux_conn_rate_ctr_desc),
.ctr_desc = all_osmux_conn_rate_ctr_desc
};
/*! allocate global rate counters
* (called once at startup).
* \param[in] cfg mgw configuration for which the rate counters are allocated.
@@ -199,7 +191,6 @@ int mgcp_ratectr_trunk_alloc(struct mgcp_trunk *trunk)
static atomic_uint mdcx_rate_ctr_index = 0;
static atomic_uint dlcx_rate_ctr_index = 0;
static atomic_uint all_rtp_conn_rate_ctr_index = 0;
static atomic_uint all_osmux_conn_rate_ctr_index = 0;
char ctr_name[256];
if (ratectr->mgcp_crcx_ctr_group == NULL) {
@@ -238,15 +229,6 @@ int mgcp_ratectr_trunk_alloc(struct mgcp_trunk *trunk)
trunk->trunk_nr);
rate_ctr_group_set_name(ratectr->all_rtp_conn_stats, ctr_name);
}
if (ratectr->all_osmux_conn_stats == NULL) {
ratectr->all_osmux_conn_stats = rate_ctr_group_alloc(trunk, &all_osmux_conn_rate_ctr_group_desc,
all_osmux_conn_rate_ctr_index++);
if (!ratectr->all_osmux_conn_stats)
return -EINVAL;
snprintf(ctr_name, sizeof(ctr_name), "%s-%u:osmux_conn", mgcp_trunk_type_strs_str(trunk->trunk_type),
trunk->trunk_nr);
rate_ctr_group_set_name(ratectr->all_osmux_conn_stats, ctr_name);
}
/* E1 specific */
if (trunk->trunk_type == MGCP_TRUNK_E1 && ratectr->e1_stats == NULL) {
@@ -283,10 +265,6 @@ void mgcp_ratectr_trunk_free(struct mgcp_trunk *trunk)
rate_ctr_group_free(ratectr->all_rtp_conn_stats);
ratectr->all_rtp_conn_stats = NULL;
}
if (ratectr->all_osmux_conn_stats) {
rate_ctr_group_free(ratectr->all_osmux_conn_stats);
ratectr->all_osmux_conn_stats = NULL;
}
/* E1 specific */
if (ratectr->e1_stats) {

View File

@@ -98,7 +98,7 @@ static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
/* Helper function to update codec map information with additional data from
* SDP, called from: mgcp_parse_sdp_data() */
static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
int payload_type, const char *audio_name)
int payload, const char *audio_name)
{
int i;
@@ -110,7 +110,7 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
/* Note: We can only update payload codecs that already exist
* in our codec list. If we get an unexpected payload type,
* we just drop it */
if (codecs[i].payload_type != payload_type)
if (codecs[i].payload_type != payload)
continue;
if (sscanf(audio_name, "%63[^/]/%d/%d",
@@ -127,7 +127,7 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
return;
}
LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload_type,
LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload,
audio_name);
}
@@ -334,7 +334,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
void *tmp_ctx = talloc_new(NULL);
struct mgcp_rtp_end *rtp;
int payload_type;
int payload;
int ptime, ptime2 = 0;
char audio_name[64];
int port, rc;
@@ -355,8 +355,8 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
/* skip these SDP attributes */
break;
case 'a':
if (sscanf(line, "a=rtpmap:%d %63s", &payload_type, audio_name) == 2) {
codecs_update(tmp_ctx, codecs, codecs_used, payload_type, audio_name);
if (sscanf(line, "a=rtpmap:%d %63s", &payload, audio_name) == 2) {
codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name);
break;
}
@@ -384,7 +384,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
case 'm':
rc = sscanf(line, "m=audio %d RTP/AVP", &port);
if (rc == 1) {
osmo_sockaddr_set_port(&rtp->addr.u.sa, port);
rtp->rtp_port = htons(port);
rtp->rtcp_port = htons(port + 1);
}
@@ -432,7 +432,7 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
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),
ntohs(rtp->rtp_port), osmo_sockaddr_ntop(&rtp->addr.u.sa, ipbuf),
rtp->packet_duration_ms);
if (codecs_used == 0)
LOGPC(DLMGCP, LOGL_NOTICE, "none");
@@ -601,7 +601,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
payload_types[0] = payload_type;
if (mgcp_conn_rtp_is_osmux(conn))
local_port = endp->trunk->cfg->osmux.local_port;
local_port = endp->trunk->cfg->osmux_port;
else
local_port = conn->end.local_port;
rc = add_audio(sdp, payload_types, 1, local_port);

View File

@@ -39,7 +39,7 @@ __attribute__((no_sanitize("undefined")))
void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss)
{
struct mgcp_rtp_state *state = &conn->state;
struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);
*expected = state->stats.cycles + state->stats.max_seq;
*expected = *expected - state->stats.base_seq + 1;
@@ -80,10 +80,10 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
int ploss;
int nchars;
struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
struct rate_ctr *octets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_RX_CTR);
struct rate_ctr *packets_tx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_TX_CTR);
struct rate_ctr *octets_tx = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_TX_CTR);
struct rate_ctr *packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);
struct rate_ctr *octets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_RX_CTR);
struct rate_ctr *packets_tx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_TX_CTR);
struct rate_ctr *octets_tx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_TX_CTR);
calc_loss(conn, &expected, &ploss);
jitter = calc_jitter(&conn->state);
@@ -99,7 +99,7 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
str += nchars;
str_len -= nchars;
if (conn->conn->endp->trunk->cfg->osmux.usage != OSMUX_USAGE_OFF) {
if (conn->conn->endp->trunk->cfg->osmux != OSMUX_USAGE_OFF) {
/* Error Counter */
nchars = snprintf(str, str_len,
"\r\nX-Osmo-CP: EC TI=%" PRIu64 ", TO=%" PRIu64,
@@ -112,12 +112,9 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
str_len -= nchars;
if (conn->osmux.state == OSMUX_STATE_ENABLED) {
struct rate_ctr *osmux_chunks_rx, *osmux_octets_rx;
osmux_chunks_rx = rate_ctr_group_get_ctr(conn->ctrg, OSMUX_CHUNKS_RX_CTR);
osmux_octets_rx = rate_ctr_group_get_ctr(conn->ctrg, OSMUX_OCTETS_RX_CTR);
snprintf(str, str_len,
"\r\nX-Osmux-ST: CR=%" PRIu64 ", BR=%" PRIu64,
osmux_chunks_rx->current, osmux_octets_rx->current);
"\r\nX-Osmux-ST: CR=%u, BR=%u",
conn->osmux.stats.chunks, conn->osmux.stats.octets);
}
}

View File

@@ -129,7 +129,7 @@ static int config_write_mgcp(struct vty *vty)
vty_out(vty, " rtp force-ptime %d%s", g_cfg->force_ptime,
VTY_NEWLINE);
switch (g_cfg->osmux.usage) {
switch (g_cfg->osmux) {
case OSMUX_USAGE_ON:
vty_out(vty, " osmux on%s", VTY_NEWLINE);
break;
@@ -141,23 +141,17 @@ static int config_write_mgcp(struct vty *vty)
vty_out(vty, " osmux off%s", VTY_NEWLINE);
break;
}
if (g_cfg->osmux.usage != OSMUX_USAGE_OFF) {
if (g_cfg->osmux.local_addr_v4)
vty_out(vty, " osmux bind-ip %s%s",
g_cfg->osmux.local_addr_v4, VTY_NEWLINE);
if (g_cfg->osmux.local_addr_v6)
vty_out(vty, " osmux bind-ip-v6 %s%s",
g_cfg->osmux.local_addr_v6, VTY_NEWLINE);
if (g_cfg->osmux) {
vty_out(vty, " osmux bind-ip %s%s",
g_cfg->osmux_addr, VTY_NEWLINE);
vty_out(vty, " osmux batch-factor %d%s",
g_cfg->osmux.batch_factor, VTY_NEWLINE);
g_cfg->osmux_batch, VTY_NEWLINE);
vty_out(vty, " osmux batch-size %u%s",
g_cfg->osmux.batch_size, VTY_NEWLINE);
g_cfg->osmux_batch_size, VTY_NEWLINE);
vty_out(vty, " osmux port %u%s",
g_cfg->osmux.local_port, VTY_NEWLINE);
g_cfg->osmux_port, VTY_NEWLINE);
vty_out(vty, " osmux dummy %s%s",
g_cfg->osmux.dummy_padding ? "on" : "off", VTY_NEWLINE);
vty_out(vty, " osmux peer-behind-nat %s%s",
g_cfg->osmux.peer_behind_nat ? "on" : "off", VTY_NEWLINE);
g_cfg->osmux_dummy ? "on" : "off", VTY_NEWLINE);
}
if (g_cfg->conn_timeout)
@@ -175,11 +169,11 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
struct rate_ctr *rx_packets, *rx_bytes;
struct rate_ctr *dropped_packets;
tx_packets = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_TX_CTR);
tx_bytes = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_TX_CTR);
rx_packets = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
rx_bytes = rate_ctr_group_get_ctr(conn->ctrg, RTP_OCTETS_RX_CTR);
dropped_packets = rate_ctr_group_get_ctr(conn->ctrg, RTP_DROPPED_PACKETS_CTR);
tx_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_TX_CTR);
tx_bytes = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_TX_CTR);
rx_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);
rx_bytes = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_OCTETS_RX_CTR);
dropped_packets = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_DROPPED_PACKETS_CTR);
vty_out(vty,
" Packets Sent: %" PRIu64 " (%" PRIu64 " bytes total)%s"
@@ -195,7 +189,7 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
rx_packets->current, rx_bytes->current, VTY_NEWLINE,
state->in_stream.err_ts_ctr->current,
state->out_stream.err_ts_ctr->current,
VTY_NEWLINE,
VTY_NEWLINE,
dropped_packets->current, VTY_NEWLINE,
codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE,
codec->frame_duration_num, codec->frame_duration_den,
@@ -203,30 +197,6 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_conn_rtp *conn)
VTY_NEWLINE, end->fmtp_extra, codec->audio_name,
codec->subtype_name, VTY_NEWLINE, end->output_enabled,
end->force_output_ptime, VTY_NEWLINE);
if (mgcp_conn_rtp_is_osmux(conn)) {
struct rate_ctr *rx_chunks, *rx_octets, *rtp_tx, *rtp_tx_dropped, *octets_tx;
rx_chunks = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_CHUNKS_RX_CTR);
rx_octets = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_OCTETS_RX_CTR);
rtp_tx = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_RTP_PACKETS_TX_CTR);
rtp_tx_dropped = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_RTP_PACKETS_TX_DROPPED_CTR);
octets_tx = rate_ctr_group_get_ctr(conn->osmux.ctrg, OSMUX_AMR_OCTETS_TX_CTR);
vty_out(vty,
" Osmux:%s"
" State: %s%s"
" Local CID: %d%s"
" Remote CID: %d%s"
" Chunks received: %" PRIu64 " (%" PRIu64 " bytes total)%s"
" RTP Packets encoded (Tx): %" PRIu64 " (%" PRIu64 " AMR octets total)%s"
" AMR payloads Dropped (Tx): %" PRIu64 "%s",
VTY_NEWLINE, osmux_state_str(conn->osmux.state), VTY_NEWLINE,
conn->osmux.local_cid_allocated ? conn->osmux.local_cid : -1, VTY_NEWLINE,
conn->osmux.remote_cid_present ? conn->osmux.remote_cid : -1, VTY_NEWLINE,
rx_chunks->current, rx_octets->current, VTY_NEWLINE,
rtp_tx->current, octets_tx->current, VTY_NEWLINE,
rtp_tx_dropped->current, VTY_NEWLINE
);
}
}
static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp,
@@ -319,14 +289,6 @@ static void dump_ratectr_trunk(struct vty *vty, struct mgcp_trunk *trunk)
" %25n: %10c (%S/s %M/m %H/h %D/d) %d",
ratectr->all_rtp_conn_stats);
}
if (ratectr->all_osmux_conn_stats) {
vty_out(vty, " %s:%s",
ratectr->all_osmux_conn_stats->desc->group_description,
VTY_NEWLINE);
vty_out_rate_ctr_group_fmt(vty,
" %25n: %10c (%S/s %M/m %H/h %D/d) %d",
ratectr->all_osmux_conn_stats);
}
if (ratectr->e1_stats && trunk->trunk_type == MGCP_TRUNK_E1) {
vty_out(vty, " %s:%s",
@@ -380,7 +342,7 @@ static int mgcp_show(struct vty *vty, int argc, const char **argv,
llist_for_each_entry(trunk, &g_cfg->trunks, entry)
dump_trunk(vty, trunk, show_stats, active_only);
if (g_cfg->osmux.usage != OSMUX_USAGE_OFF)
if (g_cfg->osmux)
vty_out(vty, "Osmux used CID: %d%s", osmux_cid_pool_count_used(),
VTY_NEWLINE);
@@ -1577,12 +1539,12 @@ DEFUN(cfg_mgcp_osmux,
OSMO_ASSERT(trunk);
if (strcmp(argv[0], "off") == 0) {
g_cfg->osmux.usage = OSMUX_USAGE_OFF;
g_cfg->osmux = OSMUX_USAGE_OFF;
return CMD_SUCCESS;
} else if (strcmp(argv[0], "on") == 0)
g_cfg->osmux.usage = OSMUX_USAGE_ON;
g_cfg->osmux = OSMUX_USAGE_ON;
else if (strcmp(argv[0], "only") == 0)
g_cfg->osmux.usage = OSMUX_USAGE_ONLY;
g_cfg->osmux = OSMUX_USAGE_ONLY;
return CMD_SUCCESS;
@@ -1590,29 +1552,12 @@ DEFUN(cfg_mgcp_osmux,
DEFUN(cfg_mgcp_osmux_ip,
cfg_mgcp_osmux_ip_cmd,
"osmux bind-ip " VTY_IPV4_CMD,
OSMUX_STR IP_STR
"IPv4 Address to bind to\n")
{
osmo_talloc_replace_string(g_cfg, &g_cfg->osmux.local_addr_v4, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_osmux_ip_v6,
cfg_mgcp_osmux_ip_v6_cmd,
"osmux bind-ip-v6 " VTY_IPV6_CMD,
"osmux bind-ip " VTY_IPV46_CMD,
OSMUX_STR IP_STR
"IPv4 Address to bind to\n"
"IPv6 Address to bind to\n")
{
osmo_talloc_replace_string(g_cfg, &g_cfg->osmux.local_addr_v6, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_osmux_port,
cfg_mgcp_osmux_port_cmd,
"osmux port <1-65535>", OSMUX_STR "port\n" "UDP port\n")
{
g_cfg->osmux.local_port = atoi(argv[0]);
osmo_strlcpy(g_cfg->osmux_addr, argv[0], sizeof(g_cfg->osmux_addr));
return CMD_SUCCESS;
}
@@ -1621,7 +1566,7 @@ DEFUN(cfg_mgcp_osmux_batch_factor,
"osmux batch-factor <1-8>",
OSMUX_STR "Batching factor\n" "Number of messages in the batch\n")
{
g_cfg->osmux.batch_factor = atoi(argv[0]);
g_cfg->osmux_batch = atoi(argv[0]);
return CMD_SUCCESS;
}
@@ -1630,7 +1575,15 @@ DEFUN(cfg_mgcp_osmux_batch_size,
"osmux batch-size <1-65535>",
OSMUX_STR "batch size\n" "Batch size in bytes\n")
{
g_cfg->osmux.batch_size = atoi(argv[0]);
g_cfg->osmux_batch_size = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_osmux_port,
cfg_mgcp_osmux_port_cmd,
"osmux port <1-65535>", OSMUX_STR "port\n" "UDP port\n")
{
g_cfg->osmux_port = atoi(argv[0]);
return CMD_SUCCESS;
}
@@ -1641,24 +1594,9 @@ DEFUN(cfg_mgcp_osmux_dummy,
"Disable dummy padding\n")
{
if (strcmp(argv[0], "on") == 0)
g_cfg->osmux.dummy_padding = true;
g_cfg->osmux_dummy = 1;
else if (strcmp(argv[0], "off") == 0)
g_cfg->osmux.dummy_padding = false;
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_osmux_peer_behind_nat,
cfg_mgcp_osmux_peer_behind_nat_cmd,
"osmux peer-behind-nat (on|off)",
OSMUX_STR "Define whether peer is behind NAT\n"
"Peer is behind NAT\n"
"Peer is NOT behind NAT\n")
{
if (strcmp(argv[0], "on") == 0)
g_cfg->osmux.peer_behind_nat = true;
else if (strcmp(argv[0], "off") == 0)
g_cfg->osmux.peer_behind_nat = false;
g_cfg->osmux_dummy = 0;
return CMD_SUCCESS;
}
@@ -1748,12 +1686,10 @@ int mgcp_vty_init(void)
install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_name_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_ip_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_ip_v6_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_port_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_factor_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_size_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_port_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd);
install_element(MGCP_NODE, &cfg_mgcp_osmux_peer_behind_nat_cmd);
install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd);
install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd);
install_element(MGCP_NODE, &cfg_mgcp_domain_cmd);
@@ -1798,9 +1734,9 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
int rc;
struct mgcp_trunk *trunk;
cfg->osmux.local_port = OSMUX_DEFAULT_PORT;
cfg->osmux.batch_factor = 4;
cfg->osmux.batch_size = OSMUX_BATCH_DEFAULT_MAX;
cfg->osmux_port = OSMUX_PORT;
cfg->osmux_batch = 4;
cfg->osmux_batch_size = OSMUX_BATCH_DEFAULT_MAX;
g_cfg = cfg;
rc = vty_read_config_file(config_file, NULL);

View File

@@ -41,6 +41,7 @@
#include <osmocom/mgcp/debug.h>
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_ctrl.h>
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
@@ -50,7 +51,7 @@
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/socket.h>
#include <osmocom/ctrl/control_if.h>
#include <osmocom/ctrl/control_vty.h>
#include <osmocom/vty/telnet_interface.h>
@@ -94,7 +95,7 @@ static char *config_file = "osmo-mgw.cfg";
/* used by msgb and mgcp */
void *tall_mgw_ctx = NULL;
static void print_help(void)
static void print_help()
{
printf("Some useful options:\n");
printf(" -h --help is printing this text.\n");
@@ -294,22 +295,13 @@ static const struct log_info_cat log_categories[] = {
.name = "DRTP",
.description = "RTP stream handling",
.color = "\033[1;30m",
.enabled = 1,
.loglevel = LOGL_NOTICE,
.enabled = 1,.loglevel = LOGL_NOTICE,
},
[DE1] = {
.name = "DE1",
.description = "E1 line handling",
.color = "\033[1;31m",
.enabled = 1,
.loglevel = LOGL_NOTICE,
},
[DOSMUX] = {
.name = "DOSMUX",
.description = "Osmux (Osmocom RTP multiplexing)",
.color = "\033[1;32m",
.enabled = 1,
.loglevel = LOGL_NOTICE,
.enabled = 1,.loglevel = LOGL_NOTICE,
},
};
@@ -365,7 +357,7 @@ int main(int argc, char **argv)
if (rc < 0)
return rc;
cfg->ctrl = ctrl_interface_setup(cfg, OSMO_CTRL_PORT_MGW, NULL);
cfg->ctrl = mgw_ctrl_interface_setup(cfg, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_MGW);
if (!cfg->ctrl) {
fprintf(stderr, "Failed to init the control interface on %s:%u. Exiting\n",
ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_MGW);

View File

@@ -24,7 +24,7 @@ EXTRA_DIST = \
mgcp_test.ok \
$(NULL)
check_PROGRAMS = \
noinst_PROGRAMS = \
mgcp_test \
$(NULL)

View File

@@ -868,7 +868,7 @@ static void test_messages(void)
printf("Connection mode not set\n");
OSMO_ASSERT(conn->end.output_enabled
== !!(conn->conn->mode & MGCP_CONN_SEND_ONLY));
== (conn->conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0));
conn->conn->mode |= CONN_UNMODIFIED;
@@ -1071,7 +1071,7 @@ static void test_packet_loss_calc(void)
"test-connection");
conn = mgcp_conn_get_rtp(&endp, _conn->id);
state = &conn->state;
packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, RTP_PACKETS_RX_CTR);
state->stats.initialized = 1;
state->stats.base_seq = pl_test_dat[i].base_seq;
@@ -1492,7 +1492,7 @@ static void test_multilple_codec(void)
conn = mgcp_conn_get_rtp(endp, conn_id);
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->end.codec->payload_type == 3);
OSMO_ASSERT(osmo_sockaddr_port(&conn->end.addr.u.sa) == 16434);
OSMO_ASSERT(conn->end.rtp_port == htons(16434));
memset(&addr, 0, sizeof(addr));
inet_aton("8.8.8.8", &addr);
OSMO_ASSERT(conn->end.addr.u.sa.sa_family == AF_INET);
@@ -1632,8 +1632,7 @@ static void test_osmux_cid(void)
for (i = 0; i < 256; ++i) {
id = osmux_cid_pool_get_next();
/* We called osmux_cid_pool_get_next() above, so next CID is i+1. */
OSMO_ASSERT(id == ((i + 1) & 0xff));
OSMO_ASSERT(id == i);
OSMO_ASSERT(osmux_cid_pool_count_used() == i + 1);
}
@@ -2079,7 +2078,7 @@ static void test_mgcp_codec_pt_translate(void)
OSMO_ASSERT(ok);
}
void test_conn_id_matching(void)
void test_conn_id_matching()
{
struct mgcp_endpoint endp = {};
struct mgcp_conn *conn;
@@ -2119,7 +2118,7 @@ void test_conn_id_matching(void)
talloc_free(conn);
}
void test_e1_trunk_nr_from_epname(void)
void test_e1_trunk_nr_from_epname()
{
unsigned int trunk_nr;
int rc;
@@ -2169,7 +2168,7 @@ void test_e1_trunk_nr_from_epname(void)
return;
}
void test_mgcp_is_rtp_dummy_payload(void)
void test_mgcp_is_rtp_dummy_payload()
{
/* realistic rtp packet */
static const char rtp_payload[] =

View File

@@ -22,7 +22,7 @@ EXTRA_DIST = \
mgcp_client_test.err \
$(NULL)
check_PROGRAMS = \
noinst_PROGRAMS = \
mgcp_client_test \
$(NULL)

View File

@@ -315,7 +315,7 @@ void test_mgcp_msg(void)
msgb_free(msg);
}
void test_mgcp_client_cancel(void)
void test_mgcp_client_cancel()
{
mgcp_trans_id_t trans_id;
struct msgb *msg;
@@ -487,7 +487,7 @@ static struct sdp_section_start_test sdp_section_start_tests[] = {
},
};
void test_sdp_section_start(void)
void test_sdp_section_start()
{
int i;
int failures = 0;